From 35c46f5932174909af0f58c954e650a882d797ad Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Tue, 24 Aug 2021 19:25:03 +0530 Subject: [PATCH 01/17] socks5 (client) udp support --- go.mod | 1 + go.sum | 8 ++++++++ intra/udp.go | 5 ++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 35f5df35..fab1cab5 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/k-sone/critbitgo v1.4.0 github.com/miekg/dns v1.1.31 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect + github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 // indirect golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 diff --git a/go.sum b/go.sum index e58c847f..80f8e196 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/oschwald/geoip2-golang v1.4.0 h1:5RlrjCgRyIGDz/mBmPfnAF4h8k0IAcRv9Pvr github.com/oschwald/geoip2-golang v1.4.0/go.mod h1:8QwxJvRImBH+Zl6Aa6MaIcs5YdlZSTKtzmPGzQqi9ng= github.com/oschwald/maxminddb-golang v1.6.0 h1:KAJSjdHQ8Kv45nFIbtoLGrGWqHFajOIm7skTyz/+Dls= github.com/oschwald/maxminddb-golang v1.6.0/go.mod h1:DUJFucBg2cvqx42YmDa/+xHvb0elJtOm3o4aFQ/nb/w= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -128,6 +130,12 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= +github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= +github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da h1:7x8pJcBTdKTBpQbRjZZc9o6CDquXBbvm9UIrR6ZSRJ4= +github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg= +github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A= +github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/intra/udp.go b/intra/udp.go index 818217a9..1bdcea05 100644 --- a/intra/udp.go +++ b/intra/udp.go @@ -37,6 +37,7 @@ import ( "github.com/eycorsican/go-tun2socks/common/log" "github.com/eycorsican/go-tun2socks/core" + "github.com/txthinking/socks5" "github.com/celzero/firestack/intra/dnscrypt" "github.com/celzero/firestack/intra/doh" @@ -436,7 +437,9 @@ func (h *udpHandler) SetProxyOptions(po *settings.ProxyOptions) error { // x.net.proxy doesn't yet support udp // https://github.com/golang/net/blob/62affa334/internal/socks/socks.go#L233 // fproxy, err = proxy.SOCKS5("udp", po.IPPort, po.Auth, proxy.Direct) - err = errors.New("udp not supported") + udptimeoutsec := 5 * 60 // 5m + tcptimeoutsec := (2 * 60 * 60) + (40 * 60) // 2h40m + fproxy, err = socks5.NewClient(po.IPPort, po.Auth.User, po.Auth.Password, tcptimeoutsec, udptimeoutsec) } else if h.httpsProxy() { err = fmt.Errorf("http-proxy not supported") } else { From 7734c4508f15d6b10f2c0b55efccb542ea34a2f0 Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Fri, 27 Aug 2021 14:40:39 +0530 Subject: [PATCH 02/17] newbies: per-app proxy and http proxy --- go.mod | 1 + go.sum | 4 + intra/android/tun2socks.go | 2 +- intra/dnsx/bravedns.go | 8 +- intra/dnsx/bravedns_test.go | 573 ++++++++++++++++++------------------ intra/protect/protect.go | 16 +- intra/settings/config.go | 105 ++++--- intra/tcp.go | 114 ++++--- intra/tunnel.go | 46 ++- intra/udp.go | 104 ++++--- 10 files changed, 527 insertions(+), 446 deletions(-) diff --git a/go.mod b/go.mod index fab1cab5..ec809194 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Jigsaw-Code/getsni v0.0.0-20190807203514-efe2dbf35d1f github.com/Jigsaw-Code/outline-ss-server v1.3.2 github.com/celzero/gotrie v0.0.0-20210413153406-d9d0dcea9cbd + github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 github.com/eycorsican/go-tun2socks v1.16.11 github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c github.com/jedisct1/go-dnsstamps v0.0.0-20200621175006-302248eecc94 diff --git a/go.sum b/go.sum index 80f8e196..a4a5a350 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,9 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 h1:lS3P5Nw3oPO05Lk2gFiYUOL3QPaH+fRoI1wFOc4G1UY= +github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/eycorsican/go-tun2socks v1.16.11 h1:+hJDNgisrYaGEqoSxhdikMgMJ4Ilfwm/IZDrWRrbaH8= github.com/eycorsican/go-tun2socks v1.16.11/go.mod h1:wgB2BFT8ZaPKyKOQ/5dljMG/YIow+AIXyq4KBwJ5sGQ= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -114,6 +117,7 @@ github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFB github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= +github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/shadowsocks/go-shadowsocks2 v0.1.4-0.20201002022019-75d43273f5a5 h1:PH+QJxWqlFTux7T1inBImjualkfKum8UKgAsRqDMmbM= github.com/shadowsocks/go-shadowsocks2 v0.1.4-0.20201002022019-75d43273f5a5/go.mod h1:/jk7XQoEyq98sd0ckJtBhaaFqfnzWm7CX/OzUAIy/Kk= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= diff --git a/intra/android/tun2socks.go b/intra/android/tun2socks.go index eab71d41..00d6edb4 100644 --- a/intra/android/tun2socks.go +++ b/intra/android/tun2socks.go @@ -57,7 +57,7 @@ func init() { // // Throws an exception if the TUN file descriptor cannot be opened, or if the tunnel fails to // connect. -func ConnectIntraTunnel(fd int, fakedns string, dohdns doh.Transport, protector protect.Protector, blocker protect.Blocker, listener intra.Listener) (intra.Tunnel, error) { +func ConnectIntraTunnel(fd int, fakedns string, dohdns doh.Transport, protector protect.Protector, blocker protect.Flow, listener intra.Listener) (intra.Tunnel, error) { tun, err := tunnel.MakeTunFile(fd) if err != nil { return nil, err diff --git a/intra/dnsx/bravedns.go b/intra/dnsx/bravedns.go index c496d40a..daa5238b 100644 --- a/intra/dnsx/bravedns.go +++ b/intra/dnsx/bravedns.go @@ -88,7 +88,7 @@ func (brave *bravedns) GetBlocklistStampHeaderKey() string { func (brave *bravedns) StampToNames(stamp string) (string, error) { if len(stamp) <= 0 { - errors.New("empty blocklist stamp") + return "", errors.New("empty blocklist stamp") } var blocklists []string @@ -147,7 +147,7 @@ func (brave *bravedns) blockUnpackedRequest(msg *dns.Msg) (r string, err error) r = strings.Join(brave.keyToNames(lists), ",") return } - err = fmt.Errorf("%v name not in blocklist %s [%s]", qname, stamp, block) + err = fmt.Errorf("%v name not in blocklist %s [%t]", qname, stamp, block) return } @@ -187,7 +187,7 @@ func (brave *bravedns) blockUnpackedResponse(msg *dns.Msg) (r string, err error) // nothing to do } } - if cnamed == false || len(ansname) <= 0 { + if !cnamed || len(ansname) <= 0 { err = fmt.Errorf("not cnamed") return } @@ -293,7 +293,7 @@ func (brave *bravedns) decode(stamp string, ver string) (tags []string, err erro stamp, err = url.PathUnescape(stamp) decoder = b64.URLEncoding } else { - err = fmt.Errorf("version does not exist", ver) + err = fmt.Errorf("version %s does not exist", ver) } if err != nil { return nil, err diff --git a/intra/dnsx/bravedns_test.go b/intra/dnsx/bravedns_test.go index 77ed07bb..372ec55a 100644 --- a/intra/dnsx/bravedns_test.go +++ b/intra/dnsx/bravedns_test.go @@ -7,311 +7,310 @@ package dnsx import ( - "fmt" - "net/url" - b64 "encoding/base64" - "encoding/binary" + b64 "encoding/base64" + "encoding/binary" + "fmt" + "net/url" ) -type bravedns struct { - flags []string - tags map[string]string +type bravelist struct { + flags []string + tags map[string]string } func main() { - fmt.Println("Hello, playground") - r, f := load() - b := bravedns{ - flags: r, - tags: f, - } - t, err := b.decode("6b%2Bg67y%2Bz7%2Fvv7%2Fvv7ztlaDvgIDkhIDnhYTogKA%3D") - t, err = b.decode("77%2Bg77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2Bg") - fmt.Println(t, err) - t, err = b.decode("4J8+v/8D///8/2DVAPAAQURxIIA=") - t, err = b.decode("4P///////////////////////////+D/") - //m, n := url.PathUnescape("4J8+v/8D///8/2DVAPAAQURxIIA=") - //x, y := b64.StdEncoding.DecodeString(m) - fmt.Println(t, err) - //fmt.Println(m, n) - //fmt.Println(x, y) + fmt.Println("Hello, playground") + r, f := load2() + b := &bravelist{ + flags: r, + tags: f, + } + t, err := b.decode("6b%2Bg67y%2Bz7%2Fvv7%2Fvv7ztlaDvgIDkhIDnhYTogKA%3D") + t, err = b.decode("77%2Bg77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2B%2F77%2Bg") + fmt.Println(t, err) + t, err = b.decode("4J8+v/8D///8/2DVAPAAQURxIIA=") + t, err = b.decode("4P///////////////////////////+D/") + //m, n := url.PathUnescape("4J8+v/8D///8/2DVAPAAQURxIIA=") + //x, y := b64.StdEncoding.DecodeString(m) + fmt.Println(t, err) + //fmt.Println(m, n) + //fmt.Println(x, y) } -func load() ([]string, map[string]string) { - obj := map[int]string{ -0: "MTF", -1: "KBI", -2: "YAC", -3: "HBP", -4: "NIM", -5: "YWG", -6: "SMQ", -7: "AQX", -8: "BTG", -9: "GUN", -10: "KSH", -11: "WAS", -12: "AZY", -13: "GWB", -14: "YMG", -15: "CZM", -16: "HYS", -17: "XIF", -18: "TQN", -19: "ZVO", -20: "YOM", -21: "THR", -22: "RPW", -23: "AMG", -24: "WTJ", -25: "ZXU", -26: "FJG", -27: "NYS", -28: "OKG", -29: "KNP", -30: "FLI", -31: "RYX", -32: "CIH", -33: "PTE", -34: "KEA", -35: "CMR", -36: "DDO", -37: "VLM", -38: "JEH", -39: "XLX", -40: "OQW", -41: "FXC", -42: "HZJ", -43: "SWK", -44: "VAM", -45: "AOS", -46: "FAL", -47: "CZK", -48: "FZB", -49: "PYW", -50: "JXA", -51: "KOR", -52: "DEP", -53: "RFX", -54: "DTT", -56: "RAF", -55: "VZP", -57: "THG", -58: "YVH", -59: "XQV", -60: "PIB", -61: "EEN", -62: "GDA", -63: "MAD", -64: "NAK", -65: "BPZ", -66: "HWO", -67: "YUC", -68: "IKY", -69: "LSS", -70: "NOE", -71: "PLR", -72: "FIT", -73: "LHX", -74: "FOF", -75: "DYA", -76: "JAN", -77: "FHQ", -78: "CMC", -79: "RKG", -80: "XMK", -81: "GAX", -82: "RFI", -83: "AZR", -84: "CEN", -85: "SPR", -86: "MZT", -87: "NHM", -88: "GLV", -89: "NUY", -90: "EDM", -91: "ZFC", -92: "DOP", -93: "XGC", -94: "OHE", -95: "MYS", -96: "IAJ", -97: "EAQ", -98: "AOC", -99: "XAT", -100: "OSE", -101: "IBB", -102: "EGX", -103: "HZD", -104: "FLW", -105: "ULZ", -106: "OFY", -107: "MLE", -108: "YER", -109: "DMC", -110: "IJO", -111: "OWW", -112: "EMY", -113: "XKM", -114: "CQT", -115: "ANW", -116: "DGE", -117: "BBS", -118: "OKW", -119: "ONV", -120: "CDE", -121: "PAL", -122: "DBP", -123: "MHP", -124: "EPR", -125: "OUU", -126: "YXS", -127: "UQK", -128: "GVI", -129: "TXJ", -130: "DPY", -131: "DUC", -132: "WYE", -133: "CGF", -134: "JRV", -135: "EOK", -136: "HQL", -137: "NNH", -138: "KRM", -139: "QKN", -140: "MPR", -141: "EOO", -142: "MDE", -143: "WWI", -144: "TTI", -145: "GFJ", -146: "WOD", -147: "YJR", -148: "WIB", -149: "NUI", -150: "XIO", -151: "OBW", -152: "YBO", -153: "TTW", -154: "NML", -155: "MIN", -156: "IFD", -157: "AMI", -158: "TZF", -159: "VKE", -160: "PWQ", -161: "KUA", -162: "FHW", -163: "AGZ", -164: "IVN", -165: "FIB", -166: "FGF", -167: "FLL", -168: "IVO", -169: "ALQ", -170: "FHM", -} +func load2() ([]string, map[string]string) { + obj := map[int]string{ + 0: "MTF", + 1: "KBI", + 2: "YAC", + 3: "HBP", + 4: "NIM", + 5: "YWG", + 6: "SMQ", + 7: "AQX", + 8: "BTG", + 9: "GUN", + 10: "KSH", + 11: "WAS", + 12: "AZY", + 13: "GWB", + 14: "YMG", + 15: "CZM", + 16: "HYS", + 17: "XIF", + 18: "TQN", + 19: "ZVO", + 20: "YOM", + 21: "THR", + 22: "RPW", + 23: "AMG", + 24: "WTJ", + 25: "ZXU", + 26: "FJG", + 27: "NYS", + 28: "OKG", + 29: "KNP", + 30: "FLI", + 31: "RYX", + 32: "CIH", + 33: "PTE", + 34: "KEA", + 35: "CMR", + 36: "DDO", + 37: "VLM", + 38: "JEH", + 39: "XLX", + 40: "OQW", + 41: "FXC", + 42: "HZJ", + 43: "SWK", + 44: "VAM", + 45: "AOS", + 46: "FAL", + 47: "CZK", + 48: "FZB", + 49: "PYW", + 50: "JXA", + 51: "KOR", + 52: "DEP", + 53: "RFX", + 54: "DTT", + 56: "RAF", + 55: "VZP", + 57: "THG", + 58: "YVH", + 59: "XQV", + 60: "PIB", + 61: "EEN", + 62: "GDA", + 63: "MAD", + 64: "NAK", + 65: "BPZ", + 66: "HWO", + 67: "YUC", + 68: "IKY", + 69: "LSS", + 70: "NOE", + 71: "PLR", + 72: "FIT", + 73: "LHX", + 74: "FOF", + 75: "DYA", + 76: "JAN", + 77: "FHQ", + 78: "CMC", + 79: "RKG", + 80: "XMK", + 81: "GAX", + 82: "RFI", + 83: "AZR", + 84: "CEN", + 85: "SPR", + 86: "MZT", + 87: "NHM", + 88: "GLV", + 89: "NUY", + 90: "EDM", + 91: "ZFC", + 92: "DOP", + 93: "XGC", + 94: "OHE", + 95: "MYS", + 96: "IAJ", + 97: "EAQ", + 98: "AOC", + 99: "XAT", + 100: "OSE", + 101: "IBB", + 102: "EGX", + 103: "HZD", + 104: "FLW", + 105: "ULZ", + 106: "OFY", + 107: "MLE", + 108: "YER", + 109: "DMC", + 110: "IJO", + 111: "OWW", + 112: "EMY", + 113: "XKM", + 114: "CQT", + 115: "ANW", + 116: "DGE", + 117: "BBS", + 118: "OKW", + 119: "ONV", + 120: "CDE", + 121: "PAL", + 122: "DBP", + 123: "MHP", + 124: "EPR", + 125: "OUU", + 126: "YXS", + 127: "UQK", + 128: "GVI", + 129: "TXJ", + 130: "DPY", + 131: "DUC", + 132: "WYE", + 133: "CGF", + 134: "JRV", + 135: "EOK", + 136: "HQL", + 137: "NNH", + 138: "KRM", + 139: "QKN", + 140: "MPR", + 141: "EOO", + 142: "MDE", + 143: "WWI", + 144: "TTI", + 145: "GFJ", + 146: "WOD", + 147: "YJR", + 148: "WIB", + 149: "NUI", + 150: "XIO", + 151: "OBW", + 152: "YBO", + 153: "TTW", + 154: "NML", + 155: "MIN", + 156: "IFD", + 157: "AMI", + 158: "TZF", + 159: "VKE", + 160: "PWQ", + 161: "KUA", + 162: "FHW", + 163: "AGZ", + 164: "IVN", + 165: "FIB", + 166: "FGF", + 167: "FLL", + 168: "IVO", + 169: "ALQ", + 170: "FHM", + } - rflags := make([]string, len(obj)) - fdata := make(map[string]string) - for key := range obj { - val := obj[key] - rflags[key] = val - fdata[val] = val - } - return rflags, fdata + rflags := make([]string, len(obj)) + fdata := make(map[string]string) + for key := range obj { + val := obj[key] + rflags[key] = val + fdata[val] = val + } + return rflags, fdata } -func (brave *bravedns) decode(s string) (tags []string, err error) { - stamp, err := url.QueryUnescape(s) - if (err != nil) { - return - } +func (brave *bravelist) decode(s string) (tags []string, err error) { + stamp, err := url.QueryUnescape(s) + if err != nil { + return + } - buf, err := b64.StdEncoding.DecodeString(stamp) - if (err != nil) { - stamp, err = url.PathUnescape(s) - buf, err = b64.StdEncoding.DecodeString(stamp) - } - if (err != nil) { - return - } + buf, err := b64.StdEncoding.DecodeString(stamp) + if err != nil { + stamp, err = url.PathUnescape(s) + buf, err = b64.StdEncoding.DecodeString(stamp) + } + if err != nil { + return + } - return brave.flagstotag(stringtouint(buf)) + return brave.flagstotag(stringtouint2(buf)) } -func stringtouint(b []byte) []uint16 { - stamp := string(b) - runedata := []rune(stamp) -data := make([]uint16, len(b)/2) -for i := range data { - // assuming little endian - data[i] = binary.LittleEndian.Uint16(b[i*2:(i+1)*2]) -} - resp := make([]uint16, len(runedata)) - for key, value := range runedata { - resp[key] = uint16(value) - } - fmt.Println(resp, data, len(b)) - return resp +func stringtouint2(b []byte) []uint16 { + stamp := string(b) + runedata := []rune(stamp) + data := make([]uint16, len(b)/2) + for i := range data { + // assuming little endian + data[i] = binary.LittleEndian.Uint16(b[i*2 : (i+1)*2]) + } + resp := make([]uint16, len(runedata)) + for key, value := range runedata { + resp[key] = uint16(value) + } + fmt.Println(resp, data, len(b)) + return resp } -func (brave *bravedns) flagstotag(flags []uint16) ([]string, error) { - // flags has to be an array of 16-bit integers. +func (brave *bravelist) flagstotag(flags []uint16) ([]string, error) { + // flags has to be an array of 16-bit integers. - // first index always contains the header - header := uint16(flags[0]) - // store of each big-endian position of set bits in header - tagIndices := []int{} - values := []string{} - var mask uint16 + // first index always contains the header + header := uint16(flags[0]) + // store of each big-endian position of set bits in header + tagIndices := []int{} + values := []string{} + var mask uint16 - // b1000,0000,0000,0000 - mask = 0x8000 + // b1000,0000,0000,0000 + mask = 0x8000 - // read first 16 header bits from msb to lsb - // and capture indices of set bits in tagIndices - for i := 0; i < 16; i++ { - if (header << i) == 0 { - break - } - if (header & mask) == mask { - tagIndices = append(tagIndices, i) - } - mask = mask >> 1 // shift to read the next msb bit - } - // the number of set bits in header must correspond to total - // blocklist "flags" excluding the header at position 0 - if len(tagIndices) != (len(flags) - 1) { - err := fmt.Errorf("%v %v flags and header mismatch", tagIndices, flags) - return nil, err - } + // read first 16 header bits from msb to lsb + // and capture indices of set bits in tagIndices + for i := 0; i < 16; i++ { + if (header << i) == 0 { + break + } + if (header & mask) == mask { + tagIndices = append(tagIndices, i) + } + mask = mask >> 1 // shift to read the next msb bit + } + // the number of set bits in header must correspond to total + // blocklist "flags" excluding the header at position 0 + if len(tagIndices) != (len(flags) - 1) { + err := fmt.Errorf("%v %v flags and header mismatch", tagIndices, flags) + return nil, err + } - // for all blocklist flags excluding the header - // figure out the blocklist-ids - for i := 1; i < len(flags); i++ { - // 16 blocklists are represented by one flag - // that is, one bit per blocklist - var flag = uint16(flags[i]) - // get the index of the current flag in the header - var index = tagIndices[i-1] - mask = 0x8000 - // for each of the 16 bits in the flag - // capture the set bits and calculate - // its actual decimal value, the blocklist-id - for j := 0; j < 16; j++ { - if (flag << j) == 0 { - break - } - if (flag & mask) == mask { - pos := (index * 16) + j - // from the decimal value which is its - // blocklist-id, fetch its metadata - values = append(values, brave.flags[pos]) - } - mask = mask >> 1 - } - } - return values, nil + // for all blocklist flags excluding the header + // figure out the blocklist-ids + for i := 1; i < len(flags); i++ { + // 16 blocklists are represented by one flag + // that is, one bit per blocklist + var flag = uint16(flags[i]) + // get the index of the current flag in the header + var index = tagIndices[i-1] + mask = 0x8000 + // for each of the 16 bits in the flag + // capture the set bits and calculate + // its actual decimal value, the blocklist-id + for j := 0; j < 16; j++ { + if (flag << j) == 0 { + break + } + if (flag & mask) == mask { + pos := (index * 16) + j + // from the decimal value which is its + // blocklist-id, fetch its metadata + values = append(values, brave.flags[pos]) + } + mask = mask >> 1 + } + } + return values, nil } - diff --git a/intra/protect/protect.go b/intra/protect/protect.go index bcd686b4..e07488b4 100644 --- a/intra/protect/protect.go +++ b/intra/protect/protect.go @@ -34,17 +34,23 @@ import ( "github.com/eycorsican/go-tun2socks/common/log" ) -// Blocker provides answers to filter network traffic. -type Blocker interface { - // Block is called on a new connection setup; return true to block the connection; - // false otherwise. +// Flow dictates network traffic rules. +type Flow interface { + // on is called on a new connection setup; return protect.ActiveNetId to forward + // the flow to the underlying active network, a valid net-id to forward it to approp + // proxy / vpn, or protect.BlockNetId to ground the flow. // source and target are string'd representation of net.TCPAddr and net.UDPAddr // depending on the protocol. Note: IPv4 and IPv6 have a very different string // representations: https://stackoverflow.com/a/48519490 // uid is -1 in case owner-uid of the connection couldn't be determined - Block(protocol int32, uid int, source string, target string) bool + On(protocol int32, uid int, source string, target string) string } +const ( + NetIdBlock = "block" + NetIdActive = "allow" +) + // Protector provides the ability to bypass a VPN on Android, pre-Lollipop. type Protector interface { // Protect a socket, i.e. exclude it from the VPN. diff --git a/intra/settings/config.go b/intra/settings/config.go index 80d0ad94..500cff27 100644 --- a/intra/settings/config.go +++ b/intra/settings/config.go @@ -6,8 +6,10 @@ package settings import ( - "golang.org/x/net/proxy" + "net/url" "strings" + + "golang.org/x/net/proxy" ) // TODO: These modes could be covered by bit-flags instead. @@ -46,14 +48,14 @@ const BlockModeSink int = 2 // from procfs before filtering const BlockModeFilterProc int = 3 -// ProxyModeNone forwards no packet. -const ProxyModeNone int = 0 +// ProxyModeNone forwards nothing. +const ProxyTypeNone int = 0 -// ProxyModeSOCKS5 forwards packets to a SOCKS5 endpoint. -const ProxyModeSOCKS5 int = 1 +// ProxyModeSOCKS5 forwards connections to a SOCKS5 endpoint. +const ProxyTypeSOCKS5 int = 1 -// ProxyModeHTTPS forwards packets to a HTTPS proxy. -const ProxyModeHTTPS int = 2 +// ProxyModeHTTPS forwards HTTP connections to a HTTP proxy. +const ProxyTypeHTTP int = 2 // TunMode specifies blocking and dns modes type TunMode struct { @@ -61,8 +63,6 @@ type TunMode struct { DNSMode int // BlockMode instructs change in firewall behaviour. BlockMode int - // ProxyMode determines where the traffic is forwarded to. - ProxyMode int } // DNSOptions define https or socks5 proxy options @@ -72,26 +72,25 @@ type DNSOptions struct { // ProxyOptions define https or socks5 proxy options type ProxyOptions struct { + Id string + Typ int Auth *proxy.Auth IPPort string } // SetMode re-assigns d to DNSMode, b to BlockMode, and p to ProxyMode -func (t *TunMode) SetMode(d int, b int, p int) { +func (t *TunMode) SetMode(d, b int) { t.DNSMode = d t.BlockMode = b - t.ProxyMode = p } // NewTunMode returns a new TunMode object. // `d` sets dns-mode. // `b` sets block-mode. -// `p` sets proxy-mode. -func NewTunMode(d int, b int, p int) *TunMode { +func NewTunMode(d, b int) *TunMode { return &TunMode{ DNSMode: d, BlockMode: b, - ProxyMode: p, } } @@ -104,58 +103,94 @@ func DefaultTunMode() *TunMode { return &TunMode{ DNSMode: DNSModeIP, BlockMode: BlockModeNone, - ProxyMode: ProxyModeNone, } } // NewDNSOptions returns a new DNSOpitons object. -func NewDNSOptions(ip string, port string) *DNSOptions { +func NewDNSOptions(ip, port string) *DNSOptions { // TODO: validate IP and port, protocol return &DNSOptions{ IPPort: ip + ":" + port, } } +func (d *DNSOptions) String() string { + ipport := strings.Split(d.IPPort, ":") + return ipport[0] + "," + ipport[1] +} + // NewAuthProxyOptions returns a new ProxyOptions object with authentication object. -func NewAuthProxyOptions(username string, password string, ip string, port string) *ProxyOptions { +func NewAuthProxyOptions(typ int, id, username, password, ip, port string) *ProxyOptions { if len(username) <= 0 || len(password) <= 0 { - return NewProxyOptions(ip, port) + return NewProxyOptions(typ, id, ip, port) } auth := proxy.Auth{ User: username, Password: password, } - // TODO: validate IP and port, protocol + // TODO: validate typ, IP and port, protocol return &ProxyOptions{ + Id: id, + Typ: typ, Auth: &auth, IPPort: ip + ":" + port, } } +// NewAuthProxyOptions returns a new ProxyOptions object with authentication object. +func NewEmptyAuthProxyOptions(id string) *ProxyOptions { + // TODO: validate typ, IP and port, protocol + return &ProxyOptions{ + Id: id, + Typ: ProxyTypeNone, + } +} + // NewProxyOptions returns a new ProxyOptions object. -func NewProxyOptions(ip string, port string) *ProxyOptions { - // TODO: validate IP and port, protocol +func NewProxyOptions(typ int, id, ip, port string) *ProxyOptions { + // TODO: validate typ, IP and port, protocol return &ProxyOptions{ + Id: id, + Typ: typ, Auth: nil, IPPort: ip + ":" + port, } } -func (d *DNSOptions) String() string { - ipport := strings.Split(d.IPPort, ":") - return ipport[0] + "," + ipport[1] +func (p *ProxyOptions) IsSocks5() bool { + return p.Typ == ProxyTypeSOCKS5 } -func (p *ProxyOptions) String() string { - ipport := strings.Split(p.IPPort, ":") - var username string - var password string - if p.Auth == nil { - username = "" - password = "" - } else { - username = p.Auth.User - password = p.Auth.Password +func (p *ProxyOptions) IsHttp() bool { + return p.Typ == ProxyTypeHTTP +} + +func (p *ProxyOptions) IsGrounded() bool { + return len(p.IPPort) == 0 || p.Typ == ProxyTypeNone +} + +func (p *ProxyOptions) Url() *url.URL { + return &url.URL{ + Scheme: p.Scheme(), + Opaque: "", + User: url.UserPassword(p.Auth.User, p.Auth.Password), + Host: p.IPPort, + } +} + +func (p *ProxyOptions) Scheme() string { + switch p.Typ { + case ProxyTypeSOCKS5: + return "socks5" + case ProxyTypeHTTP: + return "http" + case ProxyTypeNone: + return "none" + default: + return "" } - return username + "," + password + "," + ipport[0] + "," + ipport[1] +} + +func (p *ProxyOptions) String() string { + return p.Url().String() } diff --git a/intra/tcp.go b/intra/tcp.go index 50f1d843..74d407fb 100644 --- a/intra/tcp.go +++ b/intra/tcp.go @@ -26,20 +26,23 @@ package intra import ( + "errors" "fmt" "io" "net" + "sync" "time" "golang.org/x/net/proxy" + "github.com/elazarl/goproxy" "github.com/eycorsican/go-tun2socks/common/log" "github.com/eycorsican/go-tun2socks/core" "github.com/celzero/firestack/intra/dnscrypt" + "github.com/celzero/firestack/intra/doh" "github.com/celzero/firestack/intra/protect" "github.com/celzero/firestack/intra/settings" - "github.com/celzero/firestack/intra/doh" "github.com/celzero/firestack/intra/split" ) @@ -57,16 +60,18 @@ type TCPHandler interface { type tcpHandler struct { TCPHandler + sync.RWMutex + fakedns net.TCPAddr dns doh.Atomic alwaysSplitHTTPS bool dialer *net.Dialer - blocker protect.Blocker + flow protect.Flow tunMode *settings.TunMode listener TCPListener dnscrypt *dnscrypt.Proxy dnsproxy *net.TCPAddr - proxy proxy.Dialer + proxies map[string]*proxy.Dialer } // TCPSocketSummary provides information about each TCP socket, reported when it is closed. @@ -89,14 +94,15 @@ type TCPListener interface { // Connections to `fakedns` are redirected to DOH. // All other traffic is forwarded using `dialer`. // `listener` is provided with a summary of each socket when it is closed. -func NewTCPHandler(fakedns net.TCPAddr, dialer *net.Dialer, blocker protect.Blocker, +func NewTCPHandler(fakedns net.TCPAddr, dialer *net.Dialer, flow protect.Flow, tunMode *settings.TunMode, listener TCPListener) TCPHandler { return &tcpHandler{ fakedns: fakedns, dialer: dialer, - blocker: blocker, + flow: flow, tunMode: tunMode, listener: listener, + proxies: make(map[string]*proxy.Dialer, 8), } } @@ -191,12 +197,12 @@ func (h *tcpHandler) dnsOverride(conn net.Conn, addr *net.TCPAddr) bool { return false } -func (h *tcpHandler) blockConn(localConn net.Conn, target *net.TCPAddr) (block bool) { +func (h *tcpHandler) onConn(localConn net.Conn, target *net.TCPAddr) (netid string) { // BlockModeNone returns false, BlockModeSink returns true if h.tunMode.BlockMode == settings.BlockModeSink { - return true + return protect.NetIdBlock } else if h.tunMode.BlockMode == settings.BlockModeNone { - return false + return protect.NetIdActive } // Implict: BlockModeFilter or BlockModeFilterProc localtcp := localConn.(core.TCPConn) @@ -210,9 +216,9 @@ func (h *tcpHandler) blockConn(localConn net.Conn, target *net.TCPAddr) (block b } } - block = h.blocker.Block(6 /*TCP*/, uid, localaddr.String(), target.String()) + netid = h.flow.On(6 /*TCP*/, uid, localaddr.String(), target.String()) - if block { + if netid == protect.NetIdBlock { log.Infof("firewalled connection from %s:%s to %s:%s", localaddr.Network(), localaddr.String(), target.Network(), target.String()) } @@ -220,22 +226,11 @@ func (h *tcpHandler) blockConn(localConn net.Conn, target *net.TCPAddr) (block b return } -// TODO: move these to settings pkg -func (h *tcpHandler) socks5Proxy() bool { - return h.tunMode.ProxyMode == settings.ProxyModeSOCKS5 -} - -func (h *tcpHandler) httpsProxy() bool { - return h.tunMode.ProxyMode == settings.ProxyModeHTTPS -} - -func (h *tcpHandler) hasProxy() bool { - return h.proxy != nil -} - // TODO: Request upstream to make `conn` a `core.TCPConn` so we can avoid a type assertion. func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error { - if h.blockConn(conn, target) { + netid := h.onConn(conn, target) + + if netid == protect.NetIdBlock { // an error here results in a core.tcpConn.Abort return fmt.Errorf("tcp connection firewalled") } @@ -244,6 +239,17 @@ func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error { return nil } + var forwarder *proxy.Dialer + if netid != protect.NetIdActive { + h.RLock() + forwarder = h.proxies[netid] + h.RUnlock() + } + + if forwarder == nil && netid != protect.NetIdActive { + return fmt.Errorf("connection to non-existent netid %s firewalled", netid) + } + var summary TCPSocketSummary summary.ServerPort = filteredPort(target) start := time.Now() @@ -253,17 +259,17 @@ func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error { // TODO: Cancel dialing if c is closed // Ref: https://stackoverflow.com/questions/63656117/ // Ref: https://stackoverflow.com/questions/40328025 - if p := h.proxy; (h.socks5Proxy() || h.httpsProxy()) && p != nil { + if forwarder != nil { var generic net.Conn // deprecated: https://github.com/golang/go/issues/25104 - generic, err = p.Dial(target.Network(), target.String()) + generic, err = (*forwarder).Dial(target.Network(), target.String()) if generic != nil { c = generic.(*net.TCPConn) } - } else if summary.ServerPort == 443 { - if h.alwaysSplitHTTPS { + } else if summary.ServerPort == 443 || summary.ServerPort == 80 { + if summary.ServerPort == 443 && h.alwaysSplitHTTPS { // always split-dial https c, err = split.DialWithSplit(h.dialer, target) - } else { + } else { // split with retry otherwise summary.Retry = &split.RetryStats{} c, err = split.DialWithSplitRetry(h.dialer, target, summary.Retry) } @@ -308,20 +314,44 @@ func (h *tcpHandler) SetDNSOptions(do *settings.DNSOptions) error { return err } -func (h *tcpHandler) SetProxyOptions(po *settings.ProxyOptions) error { - var fproxy proxy.Dialer - var err error - if h.socks5Proxy() { - fproxy, err = proxy.SOCKS5("tcp", po.IPPort, po.Auth, proxy.Direct) - } else if h.httpsProxy() { - err = fmt.Errorf("http-proxy not supported") +func (h *tcpHandler) SetProxyOptions(po *settings.ProxyOptions) (err error) { + if po.IsEmpty() { + h.Lock() + delete(h.proxies, po.Id) + h.Unlock() + return + } + + var pd proxy.Dialer + if po.IsSocks5() { + pd, err = proxy.SOCKS5("tcp", po.IPPort, po.Auth, proxy.Direct) + } else if po.IsHttp() { + pd = newHttpProxy(po) } else { - err = fmt.Errorf("proxy mode not set") + err = errors.New("invalid proxy") } - if err != nil { - h.proxy = nil - return err + + if err != nil && pd != nil { + h.Lock() + h.proxies[po.Id] = &pd + h.Unlock() + } + + return +} + +type httpsproxy struct { + underlyingServer *goproxy.ProxyHttpServer +} + +func (p *httpsproxy) Dial(network, addr string) (c net.Conn, err error) { + return p.underlyingServer.ConnectDial(network, addr) +} + +func newHttpProxy(po *settings.ProxyOptions) proxy.Dialer { + server := goproxy.NewProxyHttpServer() + server.ConnectDial = server.NewConnectDialToProxy(po.String()) + return &httpsproxy{ + server, } - h.proxy = fproxy - return nil } diff --git a/intra/tunnel.go b/intra/tunnel.go index 2b3dd128..8130fca1 100644 --- a/intra/tunnel.go +++ b/intra/tunnel.go @@ -58,8 +58,8 @@ type Tunnel interface { // to the TUN device. The transport can be changed at any time during operation, but // must not be nil. SetDNS(doh.Transport) - // Set DNSMode, BlockMode, and ProxyMode. - SetTunMode(int, int, int) + // Set DNSMode and BlockMode. + SetTunMode(int, int) // When set to true, Intra will pre-emptively split all HTTPS connections. SetAlwaysSplitHTTPS(bool) // StartDNSCryptProxy starts a DNSCrypt proxy instance for resolvers @@ -70,11 +70,9 @@ type Tunnel interface { // GetDNSCryptProxy gets DNSCrypt proxy in-use. GetDNSCryptProxy() *dnscrypt.Proxy // StartTCPProxy starts tcp and udp forwarding proxy as dictated by current TunMode. - StartProxy(uname string, pwd string, ip string, port string) error - // GetTCPProxyOptions returns "uname,pwd,ip,port" csv - GetProxyOptions() string + SetProxy(typ int, id, uname, pwd, ip, port string) error // StartDNSProxy starts dns proxy as dictated by current TunMode. - StartDNSProxy(ip string, port string) error + StartDNSProxy(ip, port string) error // GetDNSOptions returns "ip,port" csv GetDNSProxyOptions() string // SetBraveDNS sets bravedns with various dns transports @@ -105,16 +103,16 @@ type intratunnel struct { // `tunWriter` is the downstream VPN tunnel. IntraTunnel.Disconnect() will close `tunWriter`. // `dialer` and `config` will be used for all network activity. // `listener` will be notified at the completion of every tunneled socket. -func NewTunnel(fakedns string, dohdns doh.Transport, tunWriter io.WriteCloser, dialer *net.Dialer, blocker protect.Blocker, config *net.ListenConfig, listener Listener) (Tunnel, error) { +func NewTunnel(fakedns string, dohdns doh.Transport, tunWriter io.WriteCloser, dialer *net.Dialer, flow protect.Flow, config *net.ListenConfig, listener Listener) (Tunnel, error) { if tunWriter == nil { return nil, errors.New("Must provide a valid TUN writer") } core.RegisterOutputFn(tunWriter.Write) t := &intratunnel{ - Tunnel: tunnel.NewTunnel(tunWriter, core.NewLWIPStack()), + Tunnel: tunnel.NewTunnel(tunWriter, core.NewLWIPStack()), tunmode: settings.DefaultTunMode(), } - if err := t.registerConnectionHandlers(fakedns, dialer, blocker, config, listener); err != nil { + if err := t.registerConnectionHandlers(fakedns, dialer, flow, config, listener); err != nil { return nil, err } t.SetDNS(dohdns) @@ -122,7 +120,7 @@ func NewTunnel(fakedns string, dohdns doh.Transport, tunWriter io.WriteCloser, d } // Registers Intra's custom UDP and TCP connection handlers to the tun2socks core. -func (t *intratunnel) registerConnectionHandlers(fakedns string, dialer *net.Dialer, blocker protect.Blocker, config *net.ListenConfig, listener Listener) error { +func (t *intratunnel) registerConnectionHandlers(fakedns string, dialer *net.Dialer, flow protect.Flow, config *net.ListenConfig, listener Listener) error { // RFC 4787 REQ-5 requires a timeout no shorter than 5 minutes. timeout, _ := time.ParseDuration("5m") @@ -130,14 +128,14 @@ func (t *intratunnel) registerConnectionHandlers(fakedns string, dialer *net.Dia if err != nil { return err } - t.udp = NewUDPHandler(*udpfakedns, timeout, blocker, t.tunmode, config, listener) + t.udp = NewUDPHandler(*udpfakedns, timeout, flow, t.tunmode, config, listener) core.RegisterUDPConnHandler(t.udp) tcpfakedns, err := net.ResolveTCPAddr("tcp", fakedns) if err != nil { return err } - t.tcp = NewTCPHandler(*tcpfakedns, dialer, blocker, t.tunmode, listener) + t.tcp = NewTCPHandler(*tcpfakedns, dialer, flow, t.tunmode, listener) core.RegisterTCPConnHandler(t.tcp) return nil } @@ -154,8 +152,8 @@ func (t *intratunnel) GetDNS() doh.Transport { return t.dns } -func (t *intratunnel) SetTunMode(dnsmode int, blockmode int, proxymode int) { - t.tunmode.SetMode(dnsmode, blockmode, proxymode) +func (t *intratunnel) SetTunMode(dnsmode, blockmode int) { + t.tunmode.SetMode(dnsmode, blockmode) } func (t *intratunnel) SetAlwaysSplitHTTPS(s bool) { @@ -222,25 +220,25 @@ func (t *intratunnel) GetDNSCryptProxy() *dnscrypt.Proxy { return t.dnscrypt } -func (t *intratunnel) StartProxy(uname string, pwd string, ip string, port string) (err error) { - p := settings.NewAuthProxyOptions(uname, pwd, ip, port) +func (t *intratunnel) UnsetProxy(id string) { + p := settings.NewEmptyAuthProxyOptions(id) + t.tcp.SetProxyOptions(p) + t.udp.SetProxyOptions(p) +} + +func (t *intratunnel) SetProxy(typ int, id, uname, pwd, ip, port string) (err error) { + p := settings.NewAuthProxyOptions(typ, id, uname, pwd, ip, port) if err = t.tcp.SetProxyOptions(p); err != nil { - t.proxyOptions = nil + t.UnsetProxy(id) return } - t.proxyOptions = p if err = t.udp.SetProxyOptions(p); err != nil { - // TODO: unset tcp proxy, or leave that upto the client? - t.proxyOptions = nil + t.UnsetProxy(id) return } return } -func (t *intratunnel) GetProxyOptions() string { - return t.proxyOptions.String() -} - func (t *intratunnel) SetBraveDNS(b dnsx.BraveDNS) error { doh := t.dns dnscrypt := t.dnscrypt diff --git a/intra/udp.go b/intra/udp.go index 1bdcea05..6da9af76 100644 --- a/intra/udp.go +++ b/intra/udp.go @@ -73,7 +73,7 @@ func makeTracker(conn interface{}) *tracker { type UDPHandler interface { core.UDPConnHandler SetDNS(dns doh.Transport) - blockConn(localudp core.UDPConn, target *net.UDPAddr) bool + onConn(localudp core.UDPConn, target *net.UDPAddr) string SetDNSCryptProxy(*dnscrypt.Proxy) SetProxyOptions(*settings.ProxyOptions) error SetDNSOptions(*settings.DNSOptions) error @@ -88,12 +88,12 @@ type udpHandler struct { fakedns net.UDPAddr dns doh.Transport config *net.ListenConfig - blocker protect.Blocker + flow protect.Flow tunMode *settings.TunMode listener UDPListener dnscrypt *dnscrypt.Proxy dnsproxy *net.UDPAddr - proxy proxy.Dialer + proxies map[string]*proxy.Dialer } // NewUDPHandler makes a UDP handler with Intra-style DNS redirection: @@ -102,16 +102,17 @@ type udpHandler struct { // `timeout` controls the effective NAT mapping lifetime. // `config` is used to bind new external UDP ports. // `listener` receives a summary about each UDP binding when it expires. -func NewUDPHandler(fakedns net.UDPAddr, timeout time.Duration, blocker protect.Blocker, +func NewUDPHandler(fakedns net.UDPAddr, timeout time.Duration, flow protect.Flow, tunMode *settings.TunMode, config *net.ListenConfig, listener UDPListener) UDPHandler { return &udpHandler{ timeout: timeout, udpConns: make(map[core.UDPConn]*tracker, 8), fakedns: fakedns, - blocker: blocker, + flow: flow, tunMode: tunMode, config: config, listener: listener, + proxies: make(map[string]*proxy.Dialer), } } @@ -165,21 +166,19 @@ func (h *udpHandler) fetchUDPInput(conn core.UDPConn, t *tracker) { } } -func (h *udpHandler) blockConn(localudp core.UDPConn, target *net.UDPAddr) (block bool) { +func (h *udpHandler) onConn(localudp core.UDPConn, target *net.UDPAddr) (netid string) { // BlockModeNone returns false, BlockModeSink returns true if h.tunMode.BlockMode == settings.BlockModeSink { - return true + return protect.NetIdBlock } if h.tunMode.BlockMode == settings.BlockModeNone { - return false + return protect.NetIdActive } - // Implict: BlockModeFilter or BlockModeFilterProc - localaddr := localudp.LocalAddr() //.(*net.UDPAddr) - return h.blockConnAddr(localaddr, target) + // Next-up If: BlockModeFilter or BlockModeFilterProc + return h.onNewConn(localudp.LocalAddr(), target) } -func (h *udpHandler) blockConnAddr(source *net.UDPAddr, target *net.UDPAddr) (block bool) { - +func (h *udpHandler) onNewConn(source *net.UDPAddr, target *net.UDPAddr) (netid string) { uid := -1 if h.tunMode.BlockMode == settings.BlockModeFilterProc { procEntry := settings.FindProcNetEntry("udp", source.IP, source.Port, target.IP, target.Port) @@ -188,30 +187,42 @@ func (h *udpHandler) blockConnAddr(source *net.UDPAddr, target *net.UDPAddr) (bl } } - block = h.blocker.Block(17 /*UDP*/, uid, source.String(), target.String()) + netid = h.flow.On(17 /*UDP*/, uid, source.String(), target.String()) - if block { + if netid == protect.NetIdBlock { log.Infof("firewalled udp connection from %s:%s to %s:%s", source.Network(), source.String(), target.Network(), target.String()) } - return block + + return } // Connect connects the proxy server. Note that target can be nil. func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error { - if h.blockConn(conn, target) { + netid := h.onConn(conn, target) + + if netid == protect.NetIdBlock { // an error here results in a core.udpConn.Close return fmt.Errorf("udp connection firewalled") } - proxymode := h.hasProxy() && (h.socks5Proxy() || h.httpsProxy()) + var forwarder *proxy.Dialer + if netid != protect.NetIdActive { + h.RLock() + forwarder = h.proxies[netid] + h.RUnlock() + } + + if forwarder == nil && netid != protect.NetIdActive { + return fmt.Errorf("connection to non-existent netid %s firewalled", netid) + } var c interface{} var err error - if proxymode { + if forwarder != nil { // TODO: h.httpproxy.Dial with quic // deprecated: https://github.com/golang/go/issues/25104 // FIXME: target can be nil: What happens then? - c, err = h.proxy.Dial(target.Network(), target.String()) + c, err = (*forwarder).Dial(target.Network(), target.String()) } else { bindAddr := &net.UDPAddr{IP: nil, Port: 0} c, err = h.config.ListenPacket(context.TODO(), bindAddr.Network(), bindAddr.String()) @@ -224,15 +235,17 @@ func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error { t := makeTracker(c) - if proxymode { + if forwarder != nil { t.ip = target } h.Lock() h.udpConns[conn] = t h.Unlock() + go h.fetchUDPInput(conn, t) - log.Infof("new udp proxy (mode: %s) conn to target: %s", proxymode, target.String()) + log.Infof("new udp proxy (mode: %s) conn to target: %s", (forwarder != nil), target.String()) + return nil } @@ -417,38 +430,33 @@ func (h *udpHandler) SetDNSOptions(do *settings.DNSOptions) error { return err } -// TODO: move these to settings pkg -func (h *udpHandler) socks5Proxy() bool { - return h.tunMode.ProxyMode == settings.ProxyModeSOCKS5 -} - -func (h *udpHandler) httpsProxy() bool { - return h.tunMode.ProxyMode == settings.ProxyModeHTTPS -} - -func (h *udpHandler) hasProxy() bool { - return h.proxy != nil -} +func (h *udpHandler) SetProxyOptions(po *settings.ProxyOptions) (err error) { + if po.IsEmpty() { + h.Lock() + delete(h.proxies, po.Id) + h.Unlock() + return + } -func (h *udpHandler) SetProxyOptions(po *settings.ProxyOptions) error { - var fproxy proxy.Dialer - var err error - if h.socks5Proxy() { + var pd proxy.Dialer + if po.IsSocks5() { // x.net.proxy doesn't yet support udp // https://github.com/golang/net/blob/62affa334/internal/socks/socks.go#L233 // fproxy, err = proxy.SOCKS5("udp", po.IPPort, po.Auth, proxy.Direct) - udptimeoutsec := 5 * 60 // 5m + udptimeoutsec := 5 * 60 // 5m tcptimeoutsec := (2 * 60 * 60) + (40 * 60) // 2h40m - fproxy, err = socks5.NewClient(po.IPPort, po.Auth.User, po.Auth.Password, tcptimeoutsec, udptimeoutsec) - } else if h.httpsProxy() { - err = fmt.Errorf("http-proxy not supported") + pd, err = socks5.NewClient(po.IPPort, po.Auth.User, po.Auth.Password, tcptimeoutsec, udptimeoutsec) + } else if po.IsHttp() { + // pd, err = } else { - err = errors.New("proxy mode not set") + err = errors.New("invalid proxy") } - if err != nil { - h.proxy = nil - return err + + if err != nil && pd != nil { + h.Lock() + h.proxies[po.Id] = &pd + h.Unlock() } - h.proxy = fproxy - return nil + + return } From 21ec40e21423119b2ff6cc91ab9a65122edd893f Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Fri, 27 Aug 2021 21:17:36 +0530 Subject: [PATCH 03/17] modify dnsproxy into a proper transport --- Makefile | 2 +- go.mod | 24 +++--- go.sum | 44 +++++++++++ intra/android/tun2socks.go | 4 +- intra/protect/protect.go | 8 +- intra/tcp.go | 34 ++++---- intra/tunnel.go | 51 +++++++----- intra/udp.go | 156 +++++++++++++++++++------------------ 8 files changed, 192 insertions(+), 131 deletions(-) diff --git a/Makefile b/Makefile index 822cd5d0..aacc5fd7 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ LINUX_BUILDDIR=$(BUILDDIR)/linux ANDROID_BUILD_CMD="$(GOBIND) -a -ldflags $(ANDROID_LDFLAGS) -target=android -tags android -work -o $(ANDROID_ARTIFACT)" ANDROID_OUTLINE_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/outline/android $(IMPORT_PATH)/outline/shadowsocks" -ANDROID_INTRA_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/intra $(IMPORT_PATH)/intra/android $(IMPORT_PATH)/intra/doh $(IMPORT_PATH)/intra/split $(IMPORT_PATH)/intra/protect $(IMPORT_PATH)/intra/settings $(IMPORT_PATH)/intra/dnscrypt $(IMPORT_PATH)/intra/dnsx $(IMPORT_PATH)/intra/xdns" +ANDROID_INTRA_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/intra $(IMPORT_PATH)/intra/android $(IMPORT_PATH)/intra/doh $(IMPORT_PATH)/intra/split $(IMPORT_PATH)/intra/protect $(IMPORT_PATH)/intra/settings $(IMPORT_PATH)/intra/dnscrypt $(IMPORT_PATH)/intra/dnsproxy $(IMPORT_PATH)/intra/dnsx $(IMPORT_PATH)/intra/xdns" IOS_BUILD_CMD="$(GOBIND) -a -ldflags $(LDFLAGS) -bundleid org.outline.tun2socks -target=ios/arm64 -tags ios -o $(IOS_ARTIFACT) $(IMPORT_PATH)/outline/apple $(IMPORT_PATH)/outline/shadowsocks" MACOS_BUILD_CMD="./tools/$(GOBIND) -a -ldflags $(LDFLAGS) -bundleid org.outline.tun2socks -target=ios/amd64 -tags ios -o $(MACOS_ARTIFACT) $(IMPORT_PATH)/outline/apple $(IMPORT_PATH)/outline/shadowsocks" WINDOWS_BUILD_CMD="$(XGOCMD) -ldflags $(XGO_LDFLAGS) --targets=windows/386 -dest $(WINDOWS_BUILDDIR) $(ELECTRON_PATH)" diff --git a/go.mod b/go.mod index ec809194..32ee19f2 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,22 @@ module github.com/celzero/firestack go 1.15 require ( - github.com/Jigsaw-Code/choir v1.0.1 - github.com/Jigsaw-Code/getsni v0.0.0-20190807203514-efe2dbf35d1f - github.com/Jigsaw-Code/outline-ss-server v1.3.2 + github.com/Jigsaw-Code/getsni v1.0.0 + github.com/Jigsaw-Code/outline-ss-server v1.3.5 + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect github.com/celzero/gotrie v0.0.0-20210413153406-d9d0dcea9cbd github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 github.com/eycorsican/go-tun2socks v1.16.11 - github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c - github.com/jedisct1/go-dnsstamps v0.0.0-20200621175006-302248eecc94 - github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9 + github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868 + github.com/jedisct1/go-dnsstamps v0.0.0-20210810213811-61cc83d2a354 + github.com/jedisct1/xsecretbox v0.0.0-20210813171751-8f930e127d47 github.com/k-sone/critbitgo v1.4.0 - github.com/miekg/dns v1.1.31 + github.com/miekg/dns v1.1.43 github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da - golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 - golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 // indirect - golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 - golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect - golang.org/x/sys v0.0.0-20201007165808-a893ed343c85 + golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 + golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect + golang.org/x/net v0.0.0-20210825183410-e898025ed96a + golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf ) diff --git a/go.sum b/go.sum index a4a5a350..6ca44b04 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,12 @@ github.com/Jigsaw-Code/choir v1.0.1 h1:WeRt6aTn5L+MtRNqRJ+J1RKgoO8CyXXt1dtZghy2K github.com/Jigsaw-Code/choir v1.0.1/go.mod h1:c4Wd1y1PeCajZbKZV+ZmcFGMDoduyqMCEMHW5iqzWXI= github.com/Jigsaw-Code/getsni v0.0.0-20190807203514-efe2dbf35d1f h1:PT61aMvdZPh/8L5FjmKu8DS4/VnmwSJICVZ/THpmLF0= github.com/Jigsaw-Code/getsni v0.0.0-20190807203514-efe2dbf35d1f/go.mod h1:C68VBkZJR/wcvgo6pmdlm6snMHWiLE844lXJ028Qh8Y= +github.com/Jigsaw-Code/getsni v1.0.0 h1:OUTIu7wTBi/7DMX+RkZrN7XhU3UDevTEsAWK4gsqSwE= +github.com/Jigsaw-Code/getsni v1.0.0/go.mod h1:Ps0Ec3fVMKLyAItVbMKoQFq1lDjtFQXZ+G5nRNNh/QE= github.com/Jigsaw-Code/outline-ss-server v1.3.2 h1:hZW1wSNiD0Nqp5gRL0avqQqOvqZsHhG6V9Ac0zWeCU0= github.com/Jigsaw-Code/outline-ss-server v1.3.2/go.mod h1:eXiKkyLq4AqQvpTvDL/rYUv30OeS+lxF+fQaGGKnzmY= +github.com/Jigsaw-Code/outline-ss-server v1.3.5 h1:XbrcJ9EP9qklr9eEi/Mp+UMMT7GhXgmvfxL5CjtS1DA= +github.com/Jigsaw-Code/outline-ss-server v1.3.5/go.mod h1:eXiKkyLq4AqQvpTvDL/rYUv30OeS+lxF+fQaGGKnzmY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= @@ -62,10 +66,16 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c h1:a/NQUT7AXkEfhaZ+nb7Uzqijo1Qc7C7SZpRrv+6UQDA= github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw= +github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868 h1:QZ79mRbNwYYYmiVjyv+X0NKgYE6nyN1yo3gtEFdzpiE= +github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw= github.com/jedisct1/go-dnsstamps v0.0.0-20200621175006-302248eecc94 h1:O5X61fl3p/dl+7hLDwDamJxRY6z/LwuH1XD+OyNNlxE= github.com/jedisct1/go-dnsstamps v0.0.0-20200621175006-302248eecc94/go.mod h1:128Ik0lG+DBYL6zaSgN3icmzDASeQgkSy3+Sp10trLc= +github.com/jedisct1/go-dnsstamps v0.0.0-20210810213811-61cc83d2a354 h1:sIB9mDh2spQdh95jeXF2h9uSNtObbehD0YbDCzmqbM8= +github.com/jedisct1/go-dnsstamps v0.0.0-20210810213811-61cc83d2a354/go.mod h1:t35n6rsPE3nD3RXbc5hI5Ax1ci/SSYTpx0BdMXh/1aE= github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9 h1:nGfB2s9K0GyHuNkJmXkIjP+m7je6Q6gjirr+weAEtDo= github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9/go.mod h1:MipBKo+gZlzpd1JXA1OliuwvtQizlFeu4aMAyTLh8bo= +github.com/jedisct1/xsecretbox v0.0.0-20210813171751-8f930e127d47 h1:Ow0DNq/gvOiJBbOE2xY5beyNK0pKtcpsjjD60SL+9nE= +github.com/jedisct1/xsecretbox v0.0.0-20210813171751-8f930e127d47/go.mod h1:FVoMcE35bRG6gw8u8b5OzQIySe/TfZfHRR8Fno/s1d8= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -82,6 +92,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -140,25 +152,35 @@ github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da h1:7x8pJcBTdKTBp github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg= github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A= github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210812204632-0ba0e8f03122 h1:AOT7vJYHE32m61R8d1WlcqhOO1AocesDsKpcMq+UOaA= +golang.org/x/crypto v0.0.0-20210812204632-0ba0e8f03122/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 h1:h+GZ3ubjuWaQjGe8owMGcmMVCqs0xYJtRG5y2bpHaqU= golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU= +golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -168,6 +190,11 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw= +golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -175,6 +202,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= @@ -191,18 +220,33 @@ golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201007165808-a893ed343c85 h1:v7tXcN5Dmvk08x9LWujjDQbk/26sd3IqhKa1NfaKmpM= golang.org/x/sys v0.0.0-20201007165808-a893ed343c85/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk= golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/intra/android/tun2socks.go b/intra/android/tun2socks.go index 00d6edb4..f59c24ce 100644 --- a/intra/android/tun2socks.go +++ b/intra/android/tun2socks.go @@ -57,7 +57,7 @@ func init() { // // Throws an exception if the TUN file descriptor cannot be opened, or if the tunnel fails to // connect. -func ConnectIntraTunnel(fd int, fakedns string, dohdns doh.Transport, protector protect.Protector, blocker protect.Flow, listener intra.Listener) (intra.Tunnel, error) { +func ConnectIntraTunnel(fd int, fakedns string, dohdns doh.Transport, protector protect.Protector, flow protect.Flow, listener intra.Listener) (intra.Tunnel, error) { tun, err := tunnel.MakeTunFile(fd) if err != nil { return nil, err @@ -65,7 +65,7 @@ func ConnectIntraTunnel(fd int, fakedns string, dohdns doh.Transport, protector dialer := protect.MakeDialer(protector) config := protect.MakeListenConfig(protector) - t, err := intra.NewTunnel(fakedns, dohdns, tun, dialer, blocker, config, listener) + t, err := intra.NewTunnel(fakedns, dohdns, tun, dialer, flow, config, listener) if err != nil { return nil, err } diff --git a/intra/protect/protect.go b/intra/protect/protect.go index e07488b4..14d76b43 100644 --- a/intra/protect/protect.go +++ b/intra/protect/protect.go @@ -36,9 +36,9 @@ import ( // Flow dictates network traffic rules. type Flow interface { - // on is called on a new connection setup; return protect.ActiveNetId to forward + // on is called on a new connection setup; return protect.NetIdActive to forward // the flow to the underlying active network, a valid net-id to forward it to approp - // proxy / vpn, or protect.BlockNetId to ground the flow. + // proxy / vpn, or protect.NetIdBlock to ground the flow. // source and target are string'd representation of net.TCPAddr and net.UDPAddr // depending on the protocol. Note: IPv4 and IPv6 have a very different string // representations: https://stackoverflow.com/a/48519490 @@ -98,7 +98,7 @@ func scan(ips []string, wantV4 bool) string { // by the first address of a different family if there are none of the same. func replaceIP(addr string, ips []string) (string, error) { if len(ips) == 0 { - return "", errors.New("No resolvers available") + return "", errors.New("no resolvers") } orighost, port, err := net.SplitHostPort(addr) if err != nil { @@ -106,7 +106,7 @@ func replaceIP(addr string, ips []string) (string, error) { } origip := net.ParseIP(orighost) if origip == nil { - return "", fmt.Errorf("Can't parse resolver IP: %s", orighost) + return "", fmt.Errorf("cannot parse resolver-ip: %s", orighost) } isV4 := origip.To4() != nil newIP := scan(ips, isV4) diff --git a/intra/tcp.go b/intra/tcp.go index 74d407fb..c9d1414e 100644 --- a/intra/tcp.go +++ b/intra/tcp.go @@ -40,6 +40,7 @@ import ( "github.com/eycorsican/go-tun2socks/core" "github.com/celzero/firestack/intra/dnscrypt" + "github.com/celzero/firestack/intra/dnsproxy" "github.com/celzero/firestack/intra/doh" "github.com/celzero/firestack/intra/protect" "github.com/celzero/firestack/intra/settings" @@ -55,7 +56,7 @@ type TCPHandler interface { dnsOverride(net.Conn, *net.TCPAddr) bool SetDNSCryptProxy(*dnscrypt.Proxy) SetProxyOptions(*settings.ProxyOptions) error - SetDNSOptions(*settings.DNSOptions) error + SetDNSProxy(dnsproxy.Transport) } type tcpHandler struct { @@ -70,7 +71,7 @@ type tcpHandler struct { tunMode *settings.TunMode listener TCPListener dnscrypt *dnscrypt.Proxy - dnsproxy *net.TCPAddr + dnsproxy dnsproxy.Transport proxies map[string]*proxy.Dialer } @@ -154,6 +155,11 @@ func filteredPort(addr net.Addr) int16 { } func (h *tcpHandler) isDNSProxy(addr *net.TCPAddr) bool { + if h.dnsproxy == nil { + log.Warnf("dnsproxy nil") + return false + } + if h.tunMode.DNSMode == settings.DNSModeProxyIP { return addr.IP.Equal(h.fakedns.IP) && addr.Port == h.fakedns.Port } else if h.tunMode.DNSMode == settings.DNSModeProxyPort { @@ -192,6 +198,9 @@ func (h *tcpHandler) dnsOverride(conn net.Conn, addr *net.TCPAddr) bool { } else if h.isDNSCrypt(addr) { go dnscrypt.HandleTCP(h.dnscrypt, conn) return true + } else if h.isDNSProxy(addr) { + go dnsproxy.Accept(h.dnsproxy, conn) + return true } // assert h.tunMode.DNSMode == settings.DNSModeNone return false @@ -273,13 +282,6 @@ func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error { summary.Retry = &split.RetryStats{} c, err = split.DialWithSplitRetry(h.dialer, target, summary.Retry) } - } else if summary.ServerPort == 53 && h.isDNSProxy(target) { - var generic net.Conn - target = h.dnsproxy - generic, err = h.dialer.Dial(target.Network(), target.String()) - if generic != nil { - c = generic.(*net.TCPConn) - } } else { var generic net.Conn generic, err = h.dialer.Dial(target.Network(), target.String()) @@ -308,14 +310,12 @@ func (h *tcpHandler) SetDNSCryptProxy(dcrypt *dnscrypt.Proxy) { h.dnscrypt = dcrypt } -func (h *tcpHandler) SetDNSOptions(do *settings.DNSOptions) error { - dnsaddr, err := net.ResolveTCPAddr("tcp", do.IPPort) - h.dnsproxy = dnsaddr - return err +func (h *tcpHandler) SetDNSProxy(d dnsproxy.Transport) { + h.dnsproxy = d } func (h *tcpHandler) SetProxyOptions(po *settings.ProxyOptions) (err error) { - if po.IsEmpty() { + if po.IsGrounded() { h.Lock() delete(h.proxies, po.Id) h.Unlock() @@ -340,18 +340,18 @@ func (h *tcpHandler) SetProxyOptions(po *settings.ProxyOptions) (err error) { return } -type httpsproxy struct { +type httpproxy struct { underlyingServer *goproxy.ProxyHttpServer } -func (p *httpsproxy) Dial(network, addr string) (c net.Conn, err error) { +func (p *httpproxy) Dial(network, addr string) (c net.Conn, err error) { return p.underlyingServer.ConnectDial(network, addr) } func newHttpProxy(po *settings.ProxyOptions) proxy.Dialer { server := goproxy.NewProxyHttpServer() server.ConnectDial = server.NewConnectDialToProxy(po.String()) - return &httpsproxy{ + return &httpproxy{ server, } } diff --git a/intra/tunnel.go b/intra/tunnel.go index 8130fca1..c795cd60 100644 --- a/intra/tunnel.go +++ b/intra/tunnel.go @@ -33,6 +33,7 @@ import ( "github.com/eycorsican/go-tun2socks/core" "github.com/celzero/firestack/intra/dnscrypt" + "github.com/celzero/firestack/intra/dnsproxy" "github.com/celzero/firestack/intra/dnsx" "github.com/celzero/firestack/intra/doh" "github.com/celzero/firestack/intra/protect" @@ -47,6 +48,7 @@ type Listener interface { TCPListener doh.Listener dnscrypt.Listener + dnsproxy.Listener } // Tunnel represents an Intra session. @@ -72,9 +74,9 @@ type Tunnel interface { // StartTCPProxy starts tcp and udp forwarding proxy as dictated by current TunMode. SetProxy(typ int, id, uname, pwd, ip, port string) error // StartDNSProxy starts dns proxy as dictated by current TunMode. - StartDNSProxy(ip, port string) error + StartDNSProxy(ip, port string, listener Listener) error // GetDNSOptions returns "ip,port" csv - GetDNSProxyOptions() string + GetDNSProxy() dnsproxy.Transport // SetBraveDNS sets bravedns with various dns transports SetBraveDNS(dnsx.BraveDNS) error // GetBraveDNS gets bravedns in-use by various dns transports @@ -83,14 +85,13 @@ type Tunnel interface { type intratunnel struct { tunnel.Tunnel - tcp TCPHandler - udp UDPHandler - dns doh.Transport - tunmode *settings.TunMode - dnscrypt *dnscrypt.Proxy - proxyOptions *settings.ProxyOptions - dnsOptions *settings.DNSOptions - bravedns dnsx.BraveDNS + tcp TCPHandler + udp UDPHandler + dns doh.Transport + tunmode *settings.TunMode + dnscrypt *dnscrypt.Proxy + dnsproxy dnsproxy.Transport + bravedns dnsx.BraveDNS } // NewTunnel creates a connected Intra session. @@ -105,7 +106,7 @@ type intratunnel struct { // `listener` will be notified at the completion of every tunneled socket. func NewTunnel(fakedns string, dohdns doh.Transport, tunWriter io.WriteCloser, dialer *net.Dialer, flow protect.Flow, config *net.ListenConfig, listener Listener) (Tunnel, error) { if tunWriter == nil { - return nil, errors.New("Must provide a valid TUN writer") + return nil, errors.New("invalid tunnel writer") } core.RegisterOutputFn(tunWriter.Write) t := &intratunnel{ @@ -160,21 +161,25 @@ func (t *intratunnel) SetAlwaysSplitHTTPS(s bool) { t.tcp.SetAlwaysSplitHTTPS(s) } -func (t *intratunnel) StartDNSProxy(ip string, port string) (err error) { - d := settings.NewDNSOptions(ip, port) - if err = t.tcp.SetDNSOptions(d); err == nil { - t.udp.SetDNSOptions(d) - } +func (t *intratunnel) StartDNSProxy(ip string, port string, listener Listener) (err error) { + d, err := dnsproxy.NewTransport(settings.NewDNSOptions(ip, port), listener) + if err != nil { - t.dnsOptions = nil + t.tcp.SetDNSProxy(nil) + t.udp.SetDNSProxy(nil) + t.dnsproxy = nil return } - t.dnsOptions = d + + t.tcp.SetDNSProxy(d) + t.udp.SetDNSProxy(d) + t.dnsproxy = d + return } -func (t *intratunnel) GetDNSProxyOptions() string { - return t.dnsOptions.String() +func (t *intratunnel) GetDNSProxy() dnsproxy.Transport { + return t.dnsproxy } func (t *intratunnel) StartDNSCryptProxy(resolvers string, relays string, listener Listener) (string, error) { @@ -190,6 +195,8 @@ func (t *intratunnel) StartDNSCryptProxy(resolvers string, relays string, listen } } if err != nil { + t.udp.SetDNSCryptProxy(nil) + t.tcp.SetDNSCryptProxy(nil) return "", err } t.udp.SetDNSCryptProxy(p) @@ -242,6 +249,7 @@ func (t *intratunnel) SetProxy(typ int, id, uname, pwd, ip, port string) (err er func (t *intratunnel) SetBraveDNS(b dnsx.BraveDNS) error { doh := t.dns dnscrypt := t.dnscrypt + dnsproxy := t.dnsproxy t.bravedns = b @@ -251,6 +259,9 @@ func (t *intratunnel) SetBraveDNS(b dnsx.BraveDNS) error { if dnscrypt != nil { dnscrypt.SetBraveDNS(b) } + if dnsproxy != nil { + dnsproxy.SetBraveDNS(b) + } return nil } diff --git a/intra/udp.go b/intra/udp.go index 6da9af76..dcfe9625 100644 --- a/intra/udp.go +++ b/intra/udp.go @@ -40,6 +40,7 @@ import ( "github.com/txthinking/socks5" "github.com/celzero/firestack/intra/dnscrypt" + "github.com/celzero/firestack/intra/dnsproxy" "github.com/celzero/firestack/intra/doh" "github.com/celzero/firestack/intra/protect" "github.com/celzero/firestack/intra/settings" @@ -76,7 +77,7 @@ type UDPHandler interface { onConn(localudp core.UDPConn, target *net.UDPAddr) string SetDNSCryptProxy(*dnscrypt.Proxy) SetProxyOptions(*settings.ProxyOptions) error - SetDNSOptions(*settings.DNSOptions) error + SetDNSProxy(dnsproxy.Transport) } type udpHandler struct { @@ -86,13 +87,13 @@ type udpHandler struct { timeout time.Duration udpConns map[core.UDPConn]*tracker fakedns net.UDPAddr + tunMode *settings.TunMode dns doh.Transport + dnscrypt *dnscrypt.Proxy + dnsproxy dnsproxy.Transport config *net.ListenConfig flow protect.Flow - tunMode *settings.TunMode listener UDPListener - dnscrypt *dnscrypt.Proxy - dnsproxy *net.UDPAddr proxies map[string]*proxy.Dialer } @@ -116,7 +117,8 @@ func NewUDPHandler(fakedns net.UDPAddr, timeout time.Duration, flow protect.Flow } } -func (h *udpHandler) fetchUDPInput(conn core.UDPConn, t *tracker) { +// fetchUDPInput reads from nat.conn to masqurade-write it to core.UDPConn +func (h *udpHandler) fetchUDPInput(conn core.UDPConn, nat *tracker) { buf := core.NewBytes(core.BufSize) defer func() { @@ -131,15 +133,15 @@ func (h *udpHandler) fetchUDPInput(conn core.UDPConn, t *tracker) { // FIXME: ReadFrom seems to block for 50mins+ at times: // Cancel the goroutine in such cases and close the conns - switch c := t.conn.(type) { + switch c := nat.conn.(type) { case net.PacketConn: // reads a packet from t.conn copying it to buf n, addr, err = c.ReadFrom(buf) - c.SetDeadline(time.Now().Add(h.timeout)) + c.SetDeadline(time.Now().Add(h.timeout)) // extend deadline case net.Conn: // c is already dialed-in to some addr in udpHandler.Connect n, err = c.Read(buf) - c.SetDeadline(time.Now().Add(h.timeout)) + c.SetDeadline(time.Now().Add(h.timeout)) // extend deadline default: err = errors.New("failed to read from proxy udp conn") } @@ -149,14 +151,14 @@ func (h *udpHandler) fetchUDPInput(conn core.UDPConn, t *tracker) { } var udpaddr *net.UDPAddr - if t.ip == nil && addr != nil { + if nat.ip == nil && addr != nil { udpaddr = addr.(*net.UDPAddr) } else { // overwrite source-addr as set in t.ip - udpaddr = t.ip + udpaddr = nat.ip } - t.download += int64(n) + nat.download += int64(n) // writes data to conn (tun) with addr as source _, err = conn.WriteFrom(buf[:n], udpaddr) if err != nil { @@ -249,43 +251,52 @@ func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error { return nil } -func (h *udpHandler) doDoh(dns doh.Transport, t *tracker, conn core.UDPConn, data []byte) { +func (h *udpHandler) doDNSProxy(dns dnsproxy.Transport, nat *tracker, conn core.UDPConn, data []byte) { + defer h.Close(conn) + + resp, err := dns.Query("udp", data) + + if resp != nil { + _, err = conn.WriteFrom(resp, nat.ip) + } + if err != nil { + log.Warnf("dnsproxy udp query fail: %v", err) + } +} + +func (h *udpHandler) doDoh(dns doh.Transport, nat *tracker, conn core.UDPConn, data []byte) { + defer h.Close(conn) + resp, err := dns.Query(data) if resp != nil { - _, err = conn.WriteFrom(resp, t.ip) + _, err = conn.WriteFrom(resp, nat.ip) } if err != nil { - log.Warnf("DoH query failed: %v", err) - } - // Note: Reading t.upload and t.download on this thread, while they are written on - // other threads, is theoretically a race condition. In practice, this race is - // impossible on 64-bit platforms, likely impossible on 32-bit platforms, and - // low-impact if it occurs (a mixed-use socket might be closed early). - if t.upload == 0 && t.download == 0 { - // conn was only used for this DNS query, so it's unlikely to be used again. - h.Close(conn) + log.Warnf("doh udp query fail: %v", err) } } -func (h *udpHandler) doDNSCrypt(p *dnscrypt.Proxy, t *tracker, conn core.UDPConn, data []byte) { +func (h *udpHandler) doDNSCrypt(p *dnscrypt.Proxy, nat *tracker, conn core.UDPConn, data []byte) { + defer h.Close(conn) + resp, err := dnscrypt.HandleUDP(p, data) if err != nil || resp == nil { - log.Errorf("dns-crypt udp query failed: %v", err) + log.Errorf("dnscrypt udp query fail: %v", err) } else { - _, err = conn.WriteFrom(resp, t.ip) + _, err = conn.WriteFrom(resp, nat.ip) if err != nil { - log.Errorf("dns-crypt udp query reply failed: %v", err) + log.Errorf("dnscrypt udp query reply fail: %v", err) } } - - if t.upload == 0 && t.download == 0 { - // conn was only used for this DNS query, so it's unlikely to be used again. - h.Close(conn) - } } func (h *udpHandler) isDNSProxy(addr *net.UDPAddr) bool { + if h.dnsproxy == nil { + log.Warnf("dnsproxy nil") + return false + } + if h.tunMode.DNSMode == settings.DNSModeProxyIP { return addr.IP.Equal(h.fakedns.IP) && addr.Port == h.fakedns.Port } else if h.tunMode.DNSMode == settings.DNSModeProxyPort { @@ -296,6 +307,10 @@ func (h *udpHandler) isDNSProxy(addr *net.UDPAddr) bool { } func (h *udpHandler) isDoh(addr *net.UDPAddr) bool { + if h.dns == nil { + log.Errorf("doh transport nil") + return false + } if h.tunMode.DNSMode == settings.DNSModeIP { return addr.IP.Equal(h.fakedns.IP) && addr.Port == h.fakedns.Port } else if h.tunMode.DNSMode == settings.DNSModePort { @@ -306,6 +321,11 @@ func (h *udpHandler) isDoh(addr *net.UDPAddr) bool { } func (h *udpHandler) isDNSCrypt(addr *net.UDPAddr, t *tracker) bool { + if h.dnscrypt == nil { + log.Errorf("dnscrypt nil") + return false + } + if h.tunMode.DNSMode == settings.DNSModeCryptIP { return addr.IP.Equal(h.fakedns.IP) && addr.Port == h.fakedns.Port } else if h.tunMode.DNSMode == settings.DNSModeCryptPort { @@ -315,25 +335,26 @@ func (h *udpHandler) isDNSCrypt(addr *net.UDPAddr, t *tracker) bool { return false } -func (h *udpHandler) dnsOverride(dns doh.Transport, dcrypt *dnscrypt.Proxy, - t *tracker, conn core.UDPConn, addr *net.UDPAddr, data []byte) bool { - dataCopy := append([]byte{}, data...) +func (h *udpHandler) dnsOverride(nat *tracker, conn core.UDPConn, addr *net.UDPAddr, data []byte) bool { + query := append([]byte{}, data...) + + h.RLock() + doh := h.dns + dcrypt := h.dnscrypt + dproxy := h.dnsproxy + h.RUnlock() if h.isDoh(addr) { - if dns == nil { - log.Errorf("doh transport nil") - return false - } - t.ip = addr - go h.doDoh(dns, t, conn, dataCopy) + nat.ip = addr + go h.doDoh(doh, nat, conn, query) return true - } else if h.isDNSCrypt(addr, t) { - if dcrypt == nil { - log.Errorf("dns crypt nil") - return false - } - t.ip = addr - go h.doDNSCrypt(dcrypt, t, conn, dataCopy) + } else if h.isDNSCrypt(addr, nat) { + nat.ip = addr + go h.doDNSCrypt(dcrypt, nat, conn, query) + return true + } else if h.isDNSProxy(addr) { + nat.ip = addr + go h.doDNSProxy(dproxy, nat, conn, query) return true } // assert h.tunMode.DNSMode == settings.DNSModeNone @@ -343,47 +364,35 @@ func (h *udpHandler) dnsOverride(dns doh.Transport, dcrypt *dnscrypt.Proxy, // ReceiveTo is called when data arrives from conn (tun). func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) (err error) { h.RLock() - doh := h.dns - dcrypt := h.dnscrypt - dnsproxy := h.dnsproxy - t, ok1 := h.udpConns[conn] + nat, ok1 := h.udpConns[conn] h.RUnlock() if !ok1 { return fmt.Errorf("connection %v->%v does not exists", conn.LocalAddr(), addr) } - if h.isDNSProxy(addr) { - if dnsproxy == nil { - log.Errorf("dns proxy nil") - } else { - t.ip = addr - addr = h.dnsproxy - } - } else if h.dnsOverride(doh, dcrypt, t, conn, addr, data) { + if h.dnsOverride(nat, conn, addr, data) { return nil } - t.upload += int64(len(data)) + nat.upload += int64(len(data)) - switch c := t.conn.(type) { + switch c := nat.conn.(type) { case net.PacketConn: - // Update deadline. c.SetDeadline(time.Now().Add(h.timeout)) // writes packet payload, data, to addr _, err = c.WriteTo(data, addr) case net.Conn: - // Update deadline. c.SetDeadline(time.Now().Add(h.timeout)) // c is already dialed-in to some addr in udpHandler.Connect _, err = c.Write(data) default: - err = errors.New("failed to write to proxy udp conn") + err = errors.New("failed write to udp proxy") } if err != nil { - log.Warnf("failed to forward UDP payload") - return errors.New("failed to write UDP data") + log.Warnf("failed to forward udp payload") + return errors.New("failed to write udp data") } return nil @@ -422,16 +431,14 @@ func (h *udpHandler) SetDNSCryptProxy(dcrypt *dnscrypt.Proxy) { h.Unlock() } -func (h *udpHandler) SetDNSOptions(do *settings.DNSOptions) error { +func (h *udpHandler) SetDNSProxy(dnsproxy dnsproxy.Transport) { h.Lock() - dnsaddr, err := net.ResolveUDPAddr("udp", do.IPPort) - h.dnsproxy = dnsaddr + h.dnsproxy = dnsproxy h.Unlock() - return err } func (h *udpHandler) SetProxyOptions(po *settings.ProxyOptions) (err error) { - if po.IsEmpty() { + if po.IsGrounded() { h.Lock() delete(h.proxies, po.Id) h.Unlock() @@ -443,11 +450,10 @@ func (h *udpHandler) SetProxyOptions(po *settings.ProxyOptions) (err error) { // x.net.proxy doesn't yet support udp // https://github.com/golang/net/blob/62affa334/internal/socks/socks.go#L233 // fproxy, err = proxy.SOCKS5("udp", po.IPPort, po.Auth, proxy.Direct) - udptimeoutsec := 5 * 60 // 5m - tcptimeoutsec := (2 * 60 * 60) + (40 * 60) // 2h40m - pd, err = socks5.NewClient(po.IPPort, po.Auth.User, po.Auth.Password, tcptimeoutsec, udptimeoutsec) + timeoutsec := int(h.timeout.Seconds()) + pd, err = socks5.NewClient(po.IPPort, po.Auth.User, po.Auth.Password, timeoutsec, timeoutsec) } else if po.IsHttp() { - // pd, err = + err = errors.New("http/quic proxy over udp unsupported") } else { err = errors.New("invalid proxy") } From 0fce2f315d2a29c5be83ebb91ca038b79190ba3a Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Sat, 28 Aug 2021 18:08:49 +0530 Subject: [PATCH 04/17] neater abstractions pending, but here is dnsproxy listener --- intra/dnsproxy/listener.go | 65 +++++++ intra/dnsproxy/upstream.go | 365 +++++++++++++++++++++++++++++++++++++ 2 files changed, 430 insertions(+) create mode 100644 intra/dnsproxy/listener.go create mode 100644 intra/dnsproxy/upstream.go diff --git a/intra/dnsproxy/listener.go b/intra/dnsproxy/listener.go new file mode 100644 index 00000000..76f7d951 --- /dev/null +++ b/intra/dnsproxy/listener.go @@ -0,0 +1,65 @@ +// Copyright (c) 2021 RethinkDNS and its authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package dnsproxy + +import "fmt" + +const ( + // Complete : Transaction completed successfully + Complete = iota + // SendFailed : Failed to send query + SendFailed + // ProxyError : Got an error from upstream + ProxyError + // BadQuery : Malformed input + BadQuery + // BadResponse : Response was invalid + BadResponse + // InternalError : This should never happen + InternalError +) + +type queryError struct { + status int + err error +} + +func (e *queryError) Error() string { + return e.err.Error() +} + +func (e *queryError) Unwrap() error { + return e.err +} + +type proxyError struct { + status int +} + +func (e *proxyError) Error() string { + return fmt.Sprintf("proxy request fail: %d", e.status) +} + +// Summary is a summary of a DNS transaction, reported when it is complete. +type Summary struct { + Latency float64 // Response (or failure) latency in seconds + Query []byte + Response []byte + Server string + Status int + ProxyStatus int // Zero unless Status is Complete or ProxyError + Blocklists string // csv separated list of blocklists names, if any. +} + +// A Token is an opaque handle used to match responses to queries. +type Token interface{} + +// Listener receives Summaries. +type Listener interface { + OnDNSProxyQuery(ipport string) Token + OnDNSProxyResponse(Token, *Summary) +} diff --git a/intra/dnsproxy/upstream.go b/intra/dnsproxy/upstream.go new file mode 100644 index 00000000..401e2df5 --- /dev/null +++ b/intra/dnsproxy/upstream.go @@ -0,0 +1,365 @@ +// Copyright (c) 2021 RethinkDNS and its authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package dnsproxy + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "net" + "net/http" + "time" + + "github.com/celzero/firestack/intra/dnsx" + "github.com/celzero/firestack/intra/settings" + "github.com/celzero/firestack/intra/xdns" + "github.com/eycorsican/go-tun2socks/common/log" + "golang.org/x/net/dns/dnsmessage" +) + +const timeout = 1 * time.Minute + +// Transport represents a DNS query transport. This interface is exported by gobind, +// so it has to be very simple. +type Transport interface { + // Given a DNS query (including ID), returns a DNS response with matching + // ID, or an error if no response was received. The error may be accompanied + // by a SERVFAIL response if appropriate. + Query(network string, q []byte) ([]byte, error) + // Return the server URL used to initialize this transport. + GetAddr() string + // SetBraveDNS sets bravedns variable + SetBraveDNS(dnsx.BraveDNS) +} + +// TODO: Keep a context here so that queries can be canceled. +type transport struct { + Transport + udp *net.UDPAddr + tcp *net.TCPAddr + listener Listener + bravedns dnsx.BraveDNS +} + +// NewTransport returns a DNS transport, ready for use. +func NewTransport(do *settings.DNSOptions, listener Listener) (t Transport, err error) { + udp, err := net.ResolveUDPAddr("udp", do.IPPort) + if err != nil { + return + } + + tcp, err := net.ResolveTCPAddr("tcp", do.IPPort) + if err != nil { + return + } + + t = &transport{ + udp: udp, + tcp: tcp, + listener: listener, + bravedns: nil, + } + return +} + +// Given a raw DNS query (including the query ID), this function sends the +// query. If the query is successful, it returns the response and a nil qerr. Otherwise, +// it returns a SERVFAIL response and a qerr with a status value indicating the cause. +func (t *transport) doQuery(network string, q []byte) (response []byte, blocklists string, elapsed time.Duration, qerr *queryError) { + if len(q) < 2 { + qerr = &queryError{BadQuery, fmt.Errorf("query length is %d", len(q))} + return + } + + start := time.Now() + if err := t.prepareOnDeviceBlock(); err == nil { + response, blocklists, err = t.applyBlocklists(q) + if err == nil { // blocklist applied only when err is nil + elapsed = time.Since(start) + return + } + // skipping block because err + log.Debugf("skip local block for %s with err %s", blocklists, err) + } else { + log.Debugf("forward query: no local block") + } + + response, blocklists, elapsed, qerr = t.sendRequest(network, q) + + if qerr != nil { // only on send-request errors + response = tryServfail(q) + } + + return +} + +func (t *transport) sendRequest(network string, q []byte) (response []byte, blocklists string, elapsed time.Duration, qerr *queryError) { + var conn net.Conn + var dialError error + start := time.Now() + + defer func() { + if qerr != nil { + log.Infof("query fail: %v", qerr) + } + if conn != nil { + log.Infof("%d close dnsproxy socket") + conn.Close() + } + }() + + if network == t.udp.Network() { + conn, dialError = net.DialUDP(network, nil, t.udp) + } else if network == t.tcp.Network() { + conn, dialError = net.DialTCP(network, nil, t.tcp) + } + if dialError != nil { + elapsed = time.Since(start) + qerr = &queryError{SendFailed, dialError} + return + } + + conn.SetDeadline(time.Now().Add(timeout)) + _, err := conn.Write(q) + if err != nil { + elapsed = time.Since(start) + qerr = &queryError{SendFailed, err} + return + } + + conn.SetDeadline(time.Now().Add(timeout)) // extend deadline + response, err = ioutil.ReadAll(conn) + elapsed = time.Since(start) + if err != nil { + qerr = &queryError{BadResponse, err} + return + } + + if len(response) >= 2 { + var r []byte + blocklists, r = t.resolveBlock(q, response) + if len(blocklists) > 0 && r != nil { + response = r // overwrite response when blocked + } + } else { + qerr = &queryError{BadResponse, fmt.Errorf("response length is %d", len(response))} + } + + return +} + +func (t *transport) Query(network string, q []byte) ([]byte, error) { + var token Token + if t.listener != nil { + token = t.listener.OnDNSProxyQuery(t.GetAddr()) + } + + response, blocklists, elapsed, qerr := t.doQuery(network, q) + + var err error + status := Complete + proxyStatus := http.StatusOK // ? + if qerr != nil { + err = qerr + status = qerr.status + proxyStatus = 0 + + var perr *proxyError + if errors.As(qerr.err, &perr) { + proxyStatus = perr.status + } + } + + if t.listener != nil { + t.listener.OnDNSProxyResponse(token, &Summary{ + Latency: elapsed.Seconds(), + Query: q, + Response: response, + Server: t.GetAddr(), + Status: status, + ProxyStatus: proxyStatus, + Blocklists: blocklists, + }) + } + return response, err +} + +func (t *transport) GetAddr() string { + return t.udp.String() +} + +func (t *transport) SetBraveDNS(b dnsx.BraveDNS) { + t.bravedns = b +} + +func (t *transport) prepareOnDeviceBlock() error { + b := t.bravedns + u := t.GetAddr() + + if b == nil || len(u) <= 0 { + return errors.New("t.addr or dnsx.bravedns nil") + } + + if !b.OnDeviceBlock() { + return errors.New("on-device block not set") + } + + return nil +} + +func (t *transport) applyBlocklists(q []byte) (response []byte, blocklists string, err error) { + bravedns := t.bravedns + if bravedns == nil { + err = errors.New("bravedns is nil") + return + } + blocklists, err = bravedns.BlockRequest(q) + if err != nil { + return + } + if len(blocklists) <= 0 { + err = errors.New("no blocklist applies") + return + } + + ans, err := xdns.BlockResponseFromMessage(q) + if err != nil { + return + } + + response, err = ans.Pack() + return +} + +func (t *transport) resolveBlock(q []byte, ans []byte) (blocklistNames string, blockedResponse []byte) { + bravedns := t.bravedns + if bravedns == nil { + return + } + + var err error + if !bravedns.OnDeviceBlock() { + return + } + + if blocklistNames, err = bravedns.BlockResponse(ans); err != nil { + log.Debugf("response not blocked %v", err) + return + } + + if len(blocklistNames) <= 0 { + log.Debugf("query not blocked blocklist empty") + return + } + + msg, err := xdns.BlockResponseFromMessage(q) + if err != nil { + log.Warnf("could not pack blocked dns ans %v", err) + return + } + + blockedResponse, _ = msg.Pack() + return +} + +// Perform a query using the transport, and send the response to the writer. +func forwardQuery(t Transport, q []byte, c io.Writer) error { + resp, qerr := t.Query("tcp", q) + if resp == nil && qerr != nil { + return qerr + } + + rlen := len(resp) + if rlen > math.MaxUint16 { + return fmt.Errorf("oversize response: %d", rlen) + } + + // Use a combined write to ensure atomicity. Otherwise, writes from two + // responses could be interleaved. + rlbuf := make([]byte, rlen+2) + binary.BigEndian.PutUint16(rlbuf, uint16(rlen)) + copy(rlbuf[2:], resp) + + n, err := c.Write(rlbuf) + if err != nil { + return err + } + + if int(n) != len(rlbuf) { + return fmt.Errorf("res write incomplete: %d < %d", n, len(rlbuf)) + } + return qerr +} + +// Perform a query using the transport, send the response to the writer, +// and close the writer if there was an error. +func forwardQueryAndCheck(t Transport, q []byte, c io.WriteCloser) { + if err := forwardQuery(t, q, c); err != nil { + log.Warnf("Query forwarding failed: %v", err) + c.Close() + } +} + +// Accept a DNS-over-TCP socket from a stub resolver, and connect the socket +// to this DNSTransport. +func Accept(t Transport, c io.ReadWriteCloser) { + qlbuf := make([]byte, 2) + for { + n, err := c.Read(qlbuf) + if n == 0 { + log.Debugf("tcp query socket clean shutdown") + break + } + if err != nil { + log.Warnf("error reading from tcp query socket: %v", err) + break + } + if n < 2 { + log.Warnf("incomplete query length") + break + } + qlen := binary.BigEndian.Uint16(qlbuf) + q := make([]byte, qlen) + n, err = c.Read(q) + if err != nil { + log.Warnf("error reading query: %v", err) + break + } + if n != int(qlen) { + log.Warnf("incomplete query: %d < %d", n, qlen) + break + } + go forwardQueryAndCheck(t, q, c) + } + // TODO: Cancel outstanding queries at this point. + c.Close() +} + +// FIXME: Move this to xdns pkg, see: BlockResponseFromMessage +// Servfail returns a SERVFAIL response to the query q. +func Servfail(q []byte) ([]byte, error) { + var msg dnsmessage.Message + if err := msg.Unpack(q); err != nil { + return nil, err + } + msg.Response = true + msg.RecursionAvailable = true + msg.RCode = dnsmessage.RCodeServerFailure + msg.Additionals = nil // Strip EDNS + return msg.Pack() +} + +func tryServfail(q []byte) []byte { + response, err := Servfail(q) + if err != nil { + log.Warnf("Error constructing servfail: %v", err) + } + return response +} From a6467361fd21ea211652b2adc650021b9a0febab Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Sat, 28 Aug 2021 20:50:10 +0530 Subject: [PATCH 05/17] rename: brave to rethink --- Makefile | 2 +- intra/dnscrypt/certs.go | 5 +- intra/dnscrypt/crypto.go | 16 ++-- intra/dnscrypt/intercept.go | 16 ++-- intra/dnscrypt/proxy.go | 16 ++-- intra/dnscrypt/serversInfo.go | 17 ++-- intra/dnsproxy/upstream.go | 46 +++++------ intra/doh/doh.go | 79 +++++++++---------- .../{dnsx/bravedns.go => rdns/rethinkdns.go} | 78 +++++++++--------- .../rethinkdns_test.go} | 14 ++-- intra/tunnel.go | 48 +++++------ 11 files changed, 165 insertions(+), 172 deletions(-) rename intra/{dnsx/bravedns.go => rdns/rethinkdns.go} (80%) rename intra/{dnsx/bravedns_test.go => rdns/rethinkdns_test.go} (95%) diff --git a/Makefile b/Makefile index aacc5fd7..b304c904 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ LINUX_BUILDDIR=$(BUILDDIR)/linux ANDROID_BUILD_CMD="$(GOBIND) -a -ldflags $(ANDROID_LDFLAGS) -target=android -tags android -work -o $(ANDROID_ARTIFACT)" ANDROID_OUTLINE_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/outline/android $(IMPORT_PATH)/outline/shadowsocks" -ANDROID_INTRA_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/intra $(IMPORT_PATH)/intra/android $(IMPORT_PATH)/intra/doh $(IMPORT_PATH)/intra/split $(IMPORT_PATH)/intra/protect $(IMPORT_PATH)/intra/settings $(IMPORT_PATH)/intra/dnscrypt $(IMPORT_PATH)/intra/dnsproxy $(IMPORT_PATH)/intra/dnsx $(IMPORT_PATH)/intra/xdns" +ANDROID_INTRA_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/intra $(IMPORT_PATH)/intra/android $(IMPORT_PATH)/intra/doh $(IMPORT_PATH)/intra/split $(IMPORT_PATH)/intra/protect $(IMPORT_PATH)/intra/settings $(IMPORT_PATH)/intra/dnscrypt $(IMPORT_PATH)/intra/dnsproxy $(IMPORT_PATH)/intra/rdns $(IMPORT_PATH)/intra/xdns" IOS_BUILD_CMD="$(GOBIND) -a -ldflags $(LDFLAGS) -bundleid org.outline.tun2socks -target=ios/arm64 -tags ios -o $(IOS_ARTIFACT) $(IMPORT_PATH)/outline/apple $(IMPORT_PATH)/outline/shadowsocks" MACOS_BUILD_CMD="./tools/$(GOBIND) -a -ldflags $(LDFLAGS) -bundleid org.outline.tun2socks -target=ios/amd64 -tags ios -o $(MACOS_ARTIFACT) $(IMPORT_PATH)/outline/apple $(IMPORT_PATH)/outline/shadowsocks" WINDOWS_BUILD_CMD="$(XGOCMD) -ldflags $(XGO_LDFLAGS) --targets=windows/386 -dest $(WINDOWS_BUILDDIR) $(ELECTRON_PATH)" diff --git a/intra/dnscrypt/certs.go b/intra/dnscrypt/certs.go index 1edf4eb8..8e934cd4 100644 --- a/intra/dnscrypt/certs.go +++ b/intra/dnscrypt/certs.go @@ -29,7 +29,6 @@ import ( "github.com/miekg/dns" "github.com/celzero/firestack/intra/xdns" - ) type CertInfo struct { @@ -50,7 +49,7 @@ type dnsExchangeResponse struct { func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk ed25519.PublicKey, serverAddress string, providerName string, isNew bool, relayTCPAddr *net.TCPAddr) (CertInfo, *net.TCPAddr, error) { if len(pk) != ed25519.PublicKeySize { - return CertInfo{}, nil, errors.New("Invalid public key length") + return CertInfo{}, nil, errors.New("invalid public key length") } if !strings.HasSuffix(providerName, ".") { providerName = providerName + "." @@ -167,7 +166,7 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk certCountStr = " - additional certificate" } if certInfo.CryptoConstruction == xdns.UndefinedConstruction { - return certInfo, nil, errors.New("No useable certificate found") + return certInfo, nil, errors.New("no useable certificate found") } if relayTCPAddr == nil { log.Warnf("relays for %v not supported.", *serverName) diff --git a/intra/dnscrypt/crypto.go b/intra/dnscrypt/crypto.go index b31cbd9f..1c8ce372 100644 --- a/intra/dnscrypt/crypto.go +++ b/intra/dnscrypt/crypto.go @@ -51,13 +51,13 @@ func pad(packet []byte, minSize int) []byte { func unpad(packet []byte) ([]byte, error) { for i := len(packet); ; { if i == 0 { - return nil, errors.New("Invalid padding (short packet)") + return nil, errors.New("invalid padding (short packet)") } i-- if packet[i] == 0x80 { return packet[:i], nil } else if packet[i] != 0x00 { - return nil, errors.New("Invalid padding (delimiter not found)") + return nil, errors.New("invalid padding (delimiter not found)") } } } @@ -67,7 +67,7 @@ func ComputeSharedKey(cryptoConstruction xdns.CryptoConstruction, secretKey *[32 var err error sharedKey, err = xsecretbox.SharedKey(*secretKey, *serverPk) if err != nil { - log.Warnf("[%v] Weak public key", providerName) + log.Warnf("[%v] weak public key", providerName) } } else { box.Precompute(&sharedKey, serverPk, secretKey) @@ -95,7 +95,7 @@ func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string) paddedLength = xdns.MaxDNSPacketSize } if QueryOverhead+len(packet)+1 > paddedLength { - err = errors.New("Question too large; cannot be padded") + err = errors.New("question too large; cannot be padded") return } encrypted = append(serverInfo.MagicQuery[:], publicKey[:]...) @@ -117,11 +117,11 @@ func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, sharedKey *[32]byte, encrypt if len(encrypted) < responseHeaderLen+TagSize+int(xdns.MinDNSPacketSize) || len(encrypted) > responseHeaderLen+TagSize+int(xdns.MaxDNSPacketSize) || !bytes.Equal(encrypted[:serverMagicLen], xdns.ServerMagic[:]) { - return encrypted, errors.New("Invalid message size or prefix") + return encrypted, errors.New("invalid message size or prefix") } serverNonce := encrypted[serverMagicLen:responseHeaderLen] if !bytes.Equal(nonce[:HalfNonceSize], serverNonce[:HalfNonceSize]) { - return encrypted, errors.New("Unexpected nonce") + return encrypted, errors.New("unexpected nonce") } var packet []byte var err error @@ -133,7 +133,7 @@ func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, sharedKey *[32]byte, encrypt var ok bool packet, ok = secretbox.Open(nil, encrypted[responseHeaderLen:], &xsalsaServerNonce, sharedKey) if !ok { - err = errors.New("Incorrect tag") + err = errors.New("incorrect tag") } } if err != nil { @@ -141,7 +141,7 @@ func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, sharedKey *[32]byte, encrypt } packet, err = unpad(packet) if err != nil || len(packet) < xdns.MinDNSPacketSize { - return encrypted, errors.New("Incorrect padding") + return encrypted, errors.New("incorrect padding") } return packet, nil } diff --git a/intra/dnscrypt/intercept.go b/intra/dnscrypt/intercept.go index 6b800788..65c0e928 100644 --- a/intra/dnscrypt/intercept.go +++ b/intra/dnscrypt/intercept.go @@ -18,7 +18,7 @@ import ( "errors" "strings" - "github.com/celzero/firestack/intra/dnsx" + "github.com/celzero/firestack/intra/rdns" "github.com/celzero/firestack/intra/xdns" "github.com/eycorsican/go-tun2socks/common/log" @@ -56,7 +56,7 @@ type Plugin interface { type Intercept struct { Plugin undelegatedSet *critbitgo.Trie - bravedns dnsx.BraveDNS + rethinkdns rdns.RethinkDNS state *InterceptState } @@ -81,7 +81,7 @@ func (ic *Intercept) HandleRequest(packet []byte, needsEDNS0Padding bool) ([]byt return packet, err } if len(msg.Question) != 1 { - return packet, errors.New("Unexpected number of questions") + return packet, errors.New("unexpected number of questions") } qName, err := xdns.NormalizeQName(msg.Question[0].Name) if err != nil { @@ -141,7 +141,7 @@ func (ic *Intercept) HandleResponse(packet []byte, truncate bool) ([]byte, error ic.responseBlockedByBraveDNS(packet) if state.action == ActionSynth && len(state.blocklists) > 0 { - log.Debugf("bravedns locally blocked response", state.blocklists) + log.Debugf("rethinkdns locally blocked response", state.blocklists) return packet, nil } @@ -254,7 +254,7 @@ func (ic *Intercept) blockUndelegated(msg *dns.Msg) error { // requestBlockedByBraveDNS blocks DNS names blocked by local rules. func (ic *Intercept) requestBlockedByBraveDNS(q []byte) error { state := ic.state - b := ic.bravedns + b := ic.rethinkdns if b == nil || state.action != ActionContinue || !b.OnDeviceBlock() { return nil // nothing to do. @@ -286,7 +286,7 @@ func (ic *Intercept) requestBlockedByBraveDNS(q []byte) error { // responseBlockedByBraveDNS blocks DNS names blocked by local rules. func (ic *Intercept) responseBlockedByBraveDNS(ans []byte) error { state := ic.state - b := ic.bravedns + b := ic.rethinkdns if b == nil || !b.OnDeviceBlock() { return nil // nothing to do. @@ -316,10 +316,10 @@ func (ic *Intercept) responseBlockedByBraveDNS(ans []byte) error { return nil } -func NewIntercept(set *critbitgo.Trie, bravedns dnsx.BraveDNS) *Intercept { +func NewIntercept(set *critbitgo.Trie, rethinkdns rdns.RethinkDNS) *Intercept { return &Intercept{ undelegatedSet: set, - bravedns: bravedns, + rethinkdns: rethinkdns, state: NewInterceptState(), } } diff --git a/intra/dnscrypt/proxy.go b/intra/dnscrypt/proxy.go index 897cbcf1..5a67ccac 100644 --- a/intra/dnscrypt/proxy.go +++ b/intra/dnscrypt/proxy.go @@ -26,7 +26,7 @@ import ( "sync" "time" - "github.com/celzero/firestack/intra/dnsx" + "github.com/celzero/firestack/intra/rdns" "github.com/celzero/firestack/intra/xdns" "github.com/eycorsican/go-tun2socks/common/log" @@ -222,7 +222,7 @@ type Proxy struct { listener Listener liveServers []string sigterm context.CancelFunc - bravedns dnsx.BraveDNS + rethinkdns rdns.RethinkDNS } func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) { @@ -278,7 +278,7 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl return } - intercept := NewIntercept(proxy.undelegatedSet, proxy.bravedns) + intercept := NewIntercept(proxy.undelegatedSet, proxy.rethinkdns) // serverName := "-" // needsEDNS0Padding = (serverInfo.Proto == stamps.StampProtoTypeDoH || serverInfo.Proto == stamps.StampProtoTypeTLS) needsEDNS0Padding := false @@ -497,8 +497,8 @@ func HandleTCP(proxy *Proxy, conn net.Conn) { } } -func (p *Proxy) SetBraveDNS(b dnsx.BraveDNS) { - p.bravedns = b +func (p *Proxy) SetRethinkDNS(b rdns.RethinkDNS) { + p.rethinkdns = b } // LiveServers returns csv of dnscrypt server-names currently in-use @@ -634,17 +634,17 @@ func (proxy *Proxy) AddServers(serverscsv string) (int, error) { servers := strings.Split(serverscsv, ",") for i, serverStampPair := range servers { if len(serverStampPair) == 0 { - return i, fmt.Errorf("Missing stamp for the stamp [%s] definition", serverStampPair) + return i, fmt.Errorf("missing stamp for the stamp [%s] definition", serverStampPair) } serverStamp := strings.Split(serverStampPair, "#") // TODO: skip duplicates. stamp, err := stamps.NewServerStampFromString(serverStamp[1]) if err != nil { - return i, fmt.Errorf("Stamp error for the stamp [%s] definition: [%v]", serverStampPair, err) + return i, fmt.Errorf("stamp error for the stamp [%s] definition: [%v]", serverStampPair, err) } if stamp.Proto == stamps.StampProtoTypeDoH { // TODO: Implement doh - return i, fmt.Errorf("DoH with DNSCrypt client not supported", serverStamp) + return i, fmt.Errorf("doh %s with dnscrypt-client not supported", serverStamp) } proxy.registeredServers[serverStamp[0]] = RegisteredServer{name: serverStamp[0], stamp: stamp} } diff --git a/intra/dnscrypt/serversInfo.go b/intra/dnscrypt/serversInfo.go index 81d4b320..2d0fb1c1 100644 --- a/intra/dnscrypt/serversInfo.go +++ b/intra/dnscrypt/serversInfo.go @@ -33,9 +33,8 @@ import ( ) type RegisteredServer struct { - name string - stamp stamps.ServerStamp - description string + name string + stamp stamps.ServerStamp } type ServerInfo struct { @@ -153,14 +152,14 @@ func fetchServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew } else if stamp.Proto == stamps.StampProtoTypeDoH { return fetchDoHServerInfo(proxy, name, stamp, isNew) } - return ServerInfo{}, errors.New("Unsupported protocol") + return ServerInfo{}, errors.New("unsupported protocol") } func fetchDNSCryptServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) { if len(stamp.ServerPk) != ed25519.PublicKeySize { serverPk, err := hex.DecodeString(strings.Replace(string(stamp.ServerPk), ":", "", -1)) if err != nil || len(serverPk) != ed25519.PublicKeySize { - return ServerInfo{}, fmt.Errorf("Unsupported public key for [%s]: [%s]", name, stamp.ServerPk) + return ServerInfo{}, fmt.Errorf("unsupported public key for [%s]: [%s]", name, stamp.ServerPk) } log.Warnf("Public key [%s] shouldn't be hex-encoded any more", string(stamp.ServerPk)) stamp.ServerPk = serverPk @@ -193,7 +192,7 @@ func fetchDNSCryptServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp func fetchDoHServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew bool) (ServerInfo, error) { // FIXME: custom ip-address, user-certs, and cert-pinning not supported - return ServerInfo{}, errors.New("Unsupported protocol") + return ServerInfo{}, errors.New("unsupported protocol") } func route(proxy *Proxy, name string) (*net.TCPAddr, error) { @@ -210,7 +209,7 @@ func route(proxy *Proxy, name string) (*net.TCPAddr, error) { } var relayCandidateStamp *stamps.ServerStamp if len(relayName) == 0 { - return nil, fmt.Errorf("Route declared for [%v] but an empty relay list", name) + return nil, fmt.Errorf("route declared for [%v] but an empty relay list", name) } else if relayStamp, err := stamps.NewServerStampFromString(relayName); err == nil { relayCandidateStamp = &relayStamp } else if _, err := net.ResolveTCPAddr("tcp", relayName); err == nil { @@ -220,7 +219,7 @@ func route(proxy *Proxy, name string) (*net.TCPAddr, error) { } } if relayCandidateStamp == nil { - return nil, fmt.Errorf("Undefined relay [%v] for server [%v]", relayName, name) + return nil, fmt.Errorf("undefined relay [%v] for server [%v]", relayName, name) } if relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCrypt || relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCryptRelay { @@ -230,7 +229,7 @@ func route(proxy *Proxy, name string) (*net.TCPAddr, error) { } return relayTCPAddr, nil } - return nil, fmt.Errorf("Invalid relay [%v] for server [%v]", relayName, name) + return nil, fmt.Errorf("invalid relay [%v] for server [%v]", relayName, name) } // NewServersInfo returns a new servers-info object diff --git a/intra/dnsproxy/upstream.go b/intra/dnsproxy/upstream.go index 401e2df5..1cc6057c 100644 --- a/intra/dnsproxy/upstream.go +++ b/intra/dnsproxy/upstream.go @@ -17,7 +17,7 @@ import ( "net/http" "time" - "github.com/celzero/firestack/intra/dnsx" + "github.com/celzero/firestack/intra/rdns" "github.com/celzero/firestack/intra/settings" "github.com/celzero/firestack/intra/xdns" "github.com/eycorsican/go-tun2socks/common/log" @@ -35,17 +35,17 @@ type Transport interface { Query(network string, q []byte) ([]byte, error) // Return the server URL used to initialize this transport. GetAddr() string - // SetBraveDNS sets bravedns variable - SetBraveDNS(dnsx.BraveDNS) + // SetRethinkDNS sets rethinkdns + SetRethinkDNS(rdns.RethinkDNS) } // TODO: Keep a context here so that queries can be canceled. type transport struct { Transport - udp *net.UDPAddr - tcp *net.TCPAddr - listener Listener - bravedns dnsx.BraveDNS + udp *net.UDPAddr + tcp *net.TCPAddr + listener Listener + rethinkdns rdns.RethinkDNS } // NewTransport returns a DNS transport, ready for use. @@ -61,10 +61,10 @@ func NewTransport(do *settings.DNSOptions, listener Listener) (t Transport, err } t = &transport{ - udp: udp, - tcp: tcp, - listener: listener, - bravedns: nil, + udp: udp, + tcp: tcp, + listener: listener, + rethinkdns: nil, } return } @@ -195,16 +195,16 @@ func (t *transport) GetAddr() string { return t.udp.String() } -func (t *transport) SetBraveDNS(b dnsx.BraveDNS) { - t.bravedns = b +func (t *transport) SetBraveDNS(b rdns.RethinkDNS) { + t.rethinkdns = b } func (t *transport) prepareOnDeviceBlock() error { - b := t.bravedns + b := t.rethinkdns u := t.GetAddr() if b == nil || len(u) <= 0 { - return errors.New("t.addr or dnsx.bravedns nil") + return errors.New("t.addr or rdns.rethinkdns nil") } if !b.OnDeviceBlock() { @@ -215,12 +215,12 @@ func (t *transport) prepareOnDeviceBlock() error { } func (t *transport) applyBlocklists(q []byte) (response []byte, blocklists string, err error) { - bravedns := t.bravedns - if bravedns == nil { - err = errors.New("bravedns is nil") + rdns := t.rethinkdns + if rdns == nil { + err = errors.New("rethinkdns is nil") return } - blocklists, err = bravedns.BlockRequest(q) + blocklists, err = rdns.BlockRequest(q) if err != nil { return } @@ -239,17 +239,17 @@ func (t *transport) applyBlocklists(q []byte) (response []byte, blocklists strin } func (t *transport) resolveBlock(q []byte, ans []byte) (blocklistNames string, blockedResponse []byte) { - bravedns := t.bravedns - if bravedns == nil { + rdns := t.rethinkdns + if rdns == nil { return } var err error - if !bravedns.OnDeviceBlock() { + if !rdns.OnDeviceBlock() { return } - if blocklistNames, err = bravedns.BlockResponse(ans); err != nil { + if blocklistNames, err = rdns.BlockResponse(ans); err != nil { log.Debugf("response not blocked %v", err) return } diff --git a/intra/doh/doh.go b/intra/doh/doh.go index 35a96938..22768397 100644 --- a/intra/doh/doh.go +++ b/intra/doh/doh.go @@ -41,10 +41,10 @@ import ( "sync" "time" - "github.com/celzero/firestack/intra/dnsx" - "github.com/celzero/firestack/intra/xdns" "github.com/celzero/firestack/intra/doh/ipmap" + "github.com/celzero/firestack/intra/rdns" "github.com/celzero/firestack/intra/split" + "github.com/celzero/firestack/intra/xdns" "github.com/eycorsican/go-tun2socks/common/log" "golang.org/x/net/dns/dnsmessage" ) @@ -98,28 +98,25 @@ type Transport interface { Query(q []byte) ([]byte, error) // Return the server URL used to initialize this transport. GetURL() string - // SetBraveDNS sets bravedns variable - SetBraveDNS(dnsx.BraveDNS) + // SetRethinkDNS sets rethinkdns variable + SetRethinkDNS(rdns.RethinkDNS) } // TODO: Keep a context here so that queries can be canceled. type transport struct { Transport - url string - hostname string - port int - ips ipmap.IPMap - client http.Client - dialer *net.Dialer - listener Listener - bravedns dnsx.BraveDNS + url string + hostname string + port int + ips ipmap.IPMap + client http.Client + dialer *net.Dialer + listener Listener + rethinkdns rdns.RethinkDNS hangoverLock sync.RWMutex hangoverExpiration time.Time } -// Wait up to three seconds for the TCP handshake to complete. -const tcpTimeout time.Duration = 3 * time.Second - func (t *transport) dial(network, addr string) (net.Conn, error) { log.Debugf("Dialing %s", addr) domain, portStr, err := net.SplitHostPort(addr) @@ -181,7 +178,7 @@ func NewTransport(rawurl string, addrs []string, dialer *net.Dialer, auth Client return nil, err } if parsedurl.Scheme != "https" { - return nil, fmt.Errorf("Bad scheme: %s", parsedurl.Scheme) + return nil, fmt.Errorf("bad scheme: %s", parsedurl.Scheme) } // Resolve the hostname and put those addresses first. portStr := parsedurl.Port() @@ -280,7 +277,7 @@ func (t *transport) doQuery(q []byte) (response []byte, blocklists string, serve t.hangoverLock.RUnlock() if inHangover { response = tryServfail(q) - qerr = &queryError{HTTPError, errors.New("Forwarder is in servfail hangover")} + qerr = &queryError{HTTPError, errors.New("forwarder in servfail hangover")} elapsed = time.Since(start) return } @@ -459,10 +456,10 @@ func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname response = r } } else { - qerr = &queryError{BadResponse, errors.New("Nonzero response ID")} + qerr = &queryError{BadResponse, errors.New("nonzero response-id")} } } else { - qerr = &queryError{BadResponse, fmt.Errorf("Response length is %d", len(response))} + qerr = &queryError{BadResponse, fmt.Errorf("response length is %d", len(response))} } return @@ -514,16 +511,16 @@ func (t *transport) GetURL() string { return t.url } -func (t *transport) SetBraveDNS(b dnsx.BraveDNS) { - t.bravedns = b +func (t *transport) SetRethinkDNS(b rdns.RethinkDNS) { + t.rethinkdns = b } func (t *transport) prepareOnDeviceBlock() error { - b := t.bravedns + b := t.rethinkdns u := t.url if b == nil || len(u) <= 0 { - return errors.New("t.url or dnsx.bravedns nil") + return errors.New("t.url or rethinkdns nil") } if !b.OnDeviceBlock() { @@ -534,12 +531,12 @@ func (t *transport) prepareOnDeviceBlock() error { } func (t *transport) applyBlocklists(q []byte) (response []byte, blocklists string, err error) { - bravedns := t.bravedns - if bravedns == nil { - errors.New("bravedns is nil") + rdns := t.rethinkdns + if rdns == nil { + err = errors.New("rethinkdns is nil") return } - blocklists, err = bravedns.BlockRequest(q) + blocklists, err = rdns.BlockRequest(q) if err != nil { return } @@ -558,18 +555,18 @@ func (t *transport) applyBlocklists(q []byte) (response []byte, blocklists strin } func (t *transport) resolveBlock(q []byte, res *http.Response, ans []byte) (blocklistNames string, blockedResponse []byte) { - bravedns := t.bravedns - if bravedns == nil { + rethinkdns := t.rethinkdns + if rethinkdns == nil { return } var err error - blocklistNames = t.blocklistsFromHeader(bravedns, res) - if len(blocklistNames) > 0 || bravedns.OnDeviceBlock() == false { + blocklistNames = t.blocklistsFromHeader(rethinkdns, res) + if len(blocklistNames) > 0 || !rethinkdns.OnDeviceBlock() { return } - if blocklistNames, err = bravedns.BlockResponse(ans); err != nil { + if blocklistNames, err = rethinkdns.BlockResponse(ans); err != nil { log.Debugf("response not blocked %v", err) return } @@ -585,19 +582,18 @@ func (t *transport) resolveBlock(q []byte, res *http.Response, ans []byte) (bloc return } - blockedResponse, err = msg.Pack() + blockedResponse, _ = msg.Pack() return } -func (t *transport) blocklistsFromHeader(bravedns dnsx.BraveDNS, res *http.Response) (blocklistNames string) { - blocklistStamp := res.Header.Get(bravedns.GetBlocklistStampHeaderKey()) - log.Debugf("header", res.Header) - log.Debugf("st", blocklistStamp) +func (t *transport) blocklistsFromHeader(rethinkdns rdns.RethinkDNS, res *http.Response) (blocklistNames string) { + blocklistStamp := res.Header.Get(rethinkdns.GetBlocklistStampHeaderKey()) + log.Debugf("st %s / header %s", blocklistStamp, res.Header) if len(blocklistStamp) <= 0 { return } var err error - blocklistNames, err = bravedns.StampToNames(blocklistStamp) + blocklistNames, err = rethinkdns.StampToNames(blocklistStamp) if err != nil { log.Errorf("could not resolve blocklist-stamp %v", err) return @@ -614,7 +610,7 @@ func forwardQuery(t Transport, q []byte, c io.Writer) error { } rlen := len(resp) if rlen > math.MaxUint16 { - return fmt.Errorf("Oversize response: %d", rlen) + return fmt.Errorf("oversized response: %d", rlen) } // Use a combined write to ensure atomicity. Otherwise, writes from two // responses could be interleaved. @@ -626,7 +622,7 @@ func forwardQuery(t Transport, q []byte, c io.Writer) error { return err } if int(n) != len(rlbuf) { - return fmt.Errorf("Incomplete response write: %d < %d", n, len(rlbuf)) + return fmt.Errorf("incomplete response write: %d < %d", n, len(rlbuf)) } return qerr } @@ -635,7 +631,7 @@ func forwardQuery(t Transport, q []byte, c io.Writer) error { // and close the writer if there was an error. func forwardQueryAndCheck(t Transport, q []byte, c io.WriteCloser) { if err := forwardQuery(t, q, c); err != nil { - log.Warnf("Query forwarding failed: %v", err) + log.Warnf("query forwarding failed: %v", err) c.Close() } } @@ -696,4 +692,3 @@ func tryServfail(q []byte) []byte { } return response } - diff --git a/intra/dnsx/bravedns.go b/intra/rdns/rethinkdns.go similarity index 80% rename from intra/dnsx/bravedns.go rename to intra/rdns/rethinkdns.go index daa5238b..2d5bde0b 100644 --- a/intra/dnsx/bravedns.go +++ b/intra/rdns/rethinkdns.go @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package dnsx +package rdns import ( b64 "encoding/base64" @@ -31,7 +31,7 @@ const ( remoteBlock = 1 ) -type BraveDNS interface { +type RethinkDNS interface { // Mode OnDeviceBlock() bool @@ -51,8 +51,8 @@ type BraveDNS interface { BlockResponse([]byte) (string, error) } -type bravedns struct { - BraveDNS +type rethinkdns struct { + RethinkDNS trie *trie.FrozenTrie flags []string tags map[string]string @@ -60,33 +60,33 @@ type bravedns struct { stamp string } -func (brave *bravedns) OnDeviceBlock() bool { - return brave.mode == localBlock +func (rdns *rethinkdns) OnDeviceBlock() bool { + return rdns.mode == localBlock } -func (brave *bravedns) GetStamp() (s string, err error) { - if len(brave.stamp) <= 0 { +func (rdns *rethinkdns) GetStamp() (s string, err error) { + if len(rdns.stamp) <= 0 { err = errors.New("no stamp") return } - s = brave.stamp + s = rdns.stamp return } -func (brave *bravedns) SetStamp(stamp string) error { +func (rdns *rethinkdns) SetStamp(stamp string) error { // validate - if _, err := brave.StampToNames(stamp); err != nil { + if _, err := rdns.StampToNames(stamp); err != nil { return err } - brave.stamp = stamp + rdns.stamp = stamp return nil } -func (brave *bravedns) GetBlocklistStampHeaderKey() string { +func (rdns *rethinkdns) GetBlocklistStampHeaderKey() string { return http.CanonicalHeaderKey(blocklistHeaderKey) } -func (brave *bravedns) StampToNames(stamp string) (string, error) { +func (rdns *rethinkdns) StampToNames(stamp string) (string, error) { if len(stamp) <= 0 { return "", errors.New("empty blocklist stamp") } @@ -95,9 +95,9 @@ func (brave *bravedns) StampToNames(stamp string) (string, error) { var err error s := strings.Split(stamp, ":") if len(s) > 1 { - blocklists, err = brave.decode(s[1], s[0]) + blocklists, err = rdns.decode(s[1], s[0]) } else { - blocklists, err = brave.decode(stamp, "0") + blocklists, err = rdns.decode(stamp, "0") } if err != nil { @@ -107,9 +107,9 @@ func (brave *bravedns) StampToNames(stamp string) (string, error) { return strings.Join(blocklists[:], ","), nil } -func (brave *bravedns) keyToNames(list []string) (v []string) { +func (rdns *rethinkdns) keyToNames(list []string) (v []string) { for _, l := range list { - x := brave.tags[l] + x := rdns.tags[l] if len(x) > 0 { // TODO: else err? v = append(v, x) } @@ -117,20 +117,20 @@ func (brave *bravedns) keyToNames(list []string) (v []string) { return } -func (brave *bravedns) BlockRequest(q []byte) (r string, err error) { +func (rdns *rethinkdns) BlockRequest(q []byte) (r string, err error) { msg := dns.Msg{} if err = msg.Unpack(q); err != nil { return } - return brave.blockUnpackedRequest(&msg) + return rdns.blockUnpackedRequest(&msg) } -func (brave *bravedns) blockUnpackedRequest(msg *dns.Msg) (r string, err error) { +func (rdns *rethinkdns) blockUnpackedRequest(msg *dns.Msg) (r string, err error) { if len(msg.Question) != 1 { err = errors.New("one question too many") return } - stamp, err := brave.GetStamp() + stamp, err := rdns.GetStamp() if err != nil { return } @@ -141,30 +141,30 @@ func (brave *bravedns) blockUnpackedRequest(msg *dns.Msg) (r string, err error) err = fmt.Errorf("unsupported dns query type %v", qtype) return } - block, lists := brave.trie.DNlookup(qname, stamp) + block, lists := rdns.trie.DNlookup(qname, stamp) // TODO: handle empty lists as err? if block { - r = strings.Join(brave.keyToNames(lists), ",") + r = strings.Join(rdns.keyToNames(lists), ",") return } err = fmt.Errorf("%v name not in blocklist %s [%t]", qname, stamp, block) return } -func (brave *bravedns) BlockResponse(q []byte) (r string, err error) { +func (rdns *rethinkdns) BlockResponse(q []byte) (r string, err error) { msg := dns.Msg{} if err = msg.Unpack(q); err != nil { return } - return brave.blockUnpackedResponse(&msg) + return rdns.blockUnpackedResponse(&msg) } -func (brave *bravedns) blockUnpackedResponse(msg *dns.Msg) (r string, err error) { +func (rdns *rethinkdns) blockUnpackedResponse(msg *dns.Msg) (r string, err error) { if len(msg.Answer) <= 1 { err = errors.New("req at least two answers") return } - stamp, err := brave.GetStamp() + stamp, err := rdns.GetStamp() if err != nil { return } @@ -198,30 +198,30 @@ func (brave *bravedns) blockUnpackedResponse(msg *dns.Msg) (r string, err error) // err when incoming name != ascii, ignore ansname, _ = xdns.NormalizeQName(ansname) - block, lists := brave.trie.DNlookup(ansname, stamp) + block, lists := rdns.trie.DNlookup(ansname, stamp) // TODO: handle empty lists as err? if block { - r = strings.Join(brave.keyToNames(lists), ",") + r = strings.Join(rdns.keyToNames(lists), ",") return } err = fmt.Errorf("%v cloaked domain not in blocklist %s", ansname, stamp) return } -func NewBraveDNSRemote(listinfo string) (BraveDNS, error) { +func NewRethinkDNSRemote(listinfo string) (RethinkDNS, error) { flags, tags, err := load(listinfo) if err != nil { return nil, err } - return &bravedns{ + return &rethinkdns{ flags: flags, tags: tags, mode: remoteBlock, }, nil } -func NewBraveDNSLocal(t string, rank string, - conf string, listinfo string) (BraveDNS, error) { +func NewRethinkDNSLocal(t string, rank string, + conf string, listinfo string) (RethinkDNS, error) { if len(t) <= 0 || len(rank) <= 0 || len(conf) <= 0 || len(listinfo) <= 0 { return nil, errors.New("missing data, unable to build blocklist") @@ -243,7 +243,7 @@ func NewBraveDNSLocal(t string, rank string, runtime.GC() // https://docs.pi-hole.net/ftldns/blockingmode/ - return &bravedns{ + return &rethinkdns{ trie: &trie, flags: flags, tags: tags, @@ -285,7 +285,7 @@ func load(blacklistconfigjson string) ([]string, map[string]string, error) { return rflags, fdata, nil } -func (brave *bravedns) decode(stamp string, ver string) (tags []string, err error) { +func (rdns *rethinkdns) decode(stamp string, ver string) (tags []string, err error) { decoder := b64.StdEncoding if ver == "0" { stamp, err = url.QueryUnescape(stamp) @@ -313,10 +313,10 @@ func (brave *bravedns) decode(stamp string, ver string) (tags []string, err erro err = fmt.Errorf("unimplemented header stamp version %v", ver) return } - return brave.flagstotag(u16) + return rdns.flagstotag(u16) } -func (brave *bravedns) flagstotag(flags []uint16) ([]string, error) { +func (rdns *rethinkdns) flagstotag(flags []uint16) ([]string, error) { // flags has to be an array of 16-bit integers. // first index always contains the header @@ -367,7 +367,7 @@ func (brave *bravedns) flagstotag(flags []uint16) ([]string, error) { pos := (index * 16) + j // from the decimal value which is its // blocklist-id, fetch its metadata - values = append(values, brave.flags[pos]) + values = append(values, rdns.flags[pos]) } mask = mask >> 1 } diff --git a/intra/dnsx/bravedns_test.go b/intra/rdns/rethinkdns_test.go similarity index 95% rename from intra/dnsx/bravedns_test.go rename to intra/rdns/rethinkdns_test.go index 372ec55a..e2872ff9 100644 --- a/intra/dnsx/bravedns_test.go +++ b/intra/rdns/rethinkdns_test.go @@ -4,7 +4,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package dnsx +package rdns import ( b64 "encoding/base64" @@ -13,7 +13,7 @@ import ( "net/url" ) -type bravelist struct { +type rdnslist struct { flags []string tags map[string]string } @@ -21,7 +21,7 @@ type bravelist struct { func main() { fmt.Println("Hello, playground") r, f := load2() - b := &bravelist{ + b := &rdnslist{ flags: r, tags: f, } @@ -222,7 +222,7 @@ func load2() ([]string, map[string]string) { return rflags, fdata } -func (brave *bravelist) decode(s string) (tags []string, err error) { +func (rdns *rdnslist) decode(s string) (tags []string, err error) { stamp, err := url.QueryUnescape(s) if err != nil { return @@ -237,7 +237,7 @@ func (brave *bravelist) decode(s string) (tags []string, err error) { return } - return brave.flagstotag(stringtouint2(buf)) + return rdns.flagstotag(stringtouint2(buf)) } func stringtouint2(b []byte) []uint16 { @@ -256,7 +256,7 @@ func stringtouint2(b []byte) []uint16 { return resp } -func (brave *bravelist) flagstotag(flags []uint16) ([]string, error) { +func (rdns *rdnslist) flagstotag(flags []uint16) ([]string, error) { // flags has to be an array of 16-bit integers. // first index always contains the header @@ -307,7 +307,7 @@ func (brave *bravelist) flagstotag(flags []uint16) ([]string, error) { pos := (index * 16) + j // from the decimal value which is its // blocklist-id, fetch its metadata - values = append(values, brave.flags[pos]) + values = append(values, rdns.flags[pos]) } mask = mask >> 1 } diff --git a/intra/tunnel.go b/intra/tunnel.go index c795cd60..c41b83ce 100644 --- a/intra/tunnel.go +++ b/intra/tunnel.go @@ -34,9 +34,9 @@ import ( "github.com/celzero/firestack/intra/dnscrypt" "github.com/celzero/firestack/intra/dnsproxy" - "github.com/celzero/firestack/intra/dnsx" "github.com/celzero/firestack/intra/doh" "github.com/celzero/firestack/intra/protect" + "github.com/celzero/firestack/intra/rdns" "github.com/celzero/firestack/intra/settings" "github.com/celzero/firestack/tunnel" ) @@ -77,21 +77,21 @@ type Tunnel interface { StartDNSProxy(ip, port string, listener Listener) error // GetDNSOptions returns "ip,port" csv GetDNSProxy() dnsproxy.Transport - // SetBraveDNS sets bravedns with various dns transports - SetBraveDNS(dnsx.BraveDNS) error - // GetBraveDNS gets bravedns in-use by various dns transports - GetBraveDNS() dnsx.BraveDNS + // SetRethinkDNS sets rethinkdns with various dns transports + SetRethinkDNS(rdns.RethinkDNS) error + // GetRethinkDNS gets rethinkdns in-use by various dns transports + GetRethinkDNS() rdns.RethinkDNS } type intratunnel struct { tunnel.Tunnel - tcp TCPHandler - udp UDPHandler - dns doh.Transport - tunmode *settings.TunMode - dnscrypt *dnscrypt.Proxy - dnsproxy dnsproxy.Transport - bravedns dnsx.BraveDNS + tcp TCPHandler + udp UDPHandler + dns doh.Transport + tunmode *settings.TunMode + dnscrypt *dnscrypt.Proxy + dnsproxy dnsproxy.Transport + rethinkdns rdns.RethinkDNS } // NewTunnel creates a connected Intra session. @@ -142,11 +142,11 @@ func (t *intratunnel) registerConnectionHandlers(fakedns string, dialer *net.Dia } func (t *intratunnel) SetDNS(dns doh.Transport) { - bravedns := t.bravedns + rethinkdns := t.rethinkdns t.dns = dns t.udp.SetDNS(dns) t.tcp.SetDNS(dns) - dns.SetBraveDNS(bravedns) + dns.SetRethinkDNS(rethinkdns) } func (t *intratunnel) GetDNS() doh.Transport { @@ -184,7 +184,7 @@ func (t *intratunnel) GetDNSProxy() dnsproxy.Transport { func (t *intratunnel) StartDNSCryptProxy(resolvers string, relays string, listener Listener) (string, error) { var err error - bravedns := t.bravedns + rethinkdns := t.rethinkdns if t.dnscrypt != nil { return "", fmt.Errorf("only one instance of dns-crypt proxy allowed") } @@ -201,7 +201,7 @@ func (t *intratunnel) StartDNSCryptProxy(resolvers string, relays string, listen } t.udp.SetDNSCryptProxy(p) t.tcp.SetDNSCryptProxy(p) - p.SetBraveDNS(bravedns) + p.SetRethinkDNS(rethinkdns) t.dnscrypt = p return p.StartProxy() @@ -218,7 +218,7 @@ func (t *intratunnel) StopDNSCryptProxy() error { err := t.dnscrypt.StopProxy() t.udp.SetDNSCryptProxy(nil) t.tcp.SetDNSCryptProxy(nil) - t.dnscrypt.SetBraveDNS(nil) + t.dnscrypt.SetRethinkDNS(nil) t.dnscrypt = nil return err } @@ -246,26 +246,26 @@ func (t *intratunnel) SetProxy(typ int, id, uname, pwd, ip, port string) (err er return } -func (t *intratunnel) SetBraveDNS(b dnsx.BraveDNS) error { +func (t *intratunnel) SetRethinkDNS(b rdns.RethinkDNS) error { doh := t.dns dnscrypt := t.dnscrypt dnsproxy := t.dnsproxy - t.bravedns = b + t.rethinkdns = b if doh != nil { - doh.SetBraveDNS(b) + doh.SetRethinkDNS(b) } if dnscrypt != nil { - dnscrypt.SetBraveDNS(b) + dnscrypt.SetRethinkDNS(b) } if dnsproxy != nil { - dnsproxy.SetBraveDNS(b) + dnsproxy.SetRethinkDNS(b) } return nil } -func (t *intratunnel) GetBraveDNS() dnsx.BraveDNS { - return t.bravedns +func (t *intratunnel) GetRethinkDNS() rdns.RethinkDNS { + return t.rethinkdns } From bb03f70eded97ab97ce8d318d43d2aa2ac036e11 Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Mon, 30 Aug 2021 17:03:59 +0530 Subject: [PATCH 06/17] handle cname, svcb/https cloaking --- intra/doh/doh_test.go | 3 +-- intra/rdns/rethinkdns.go | 57 +++++++++++++++++++--------------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/intra/doh/doh_test.go b/intra/doh/doh_test.go index 29e8235f..a1627c58 100644 --- a/intra/doh/doh_test.go +++ b/intra/doh/doh_test.go @@ -591,7 +591,7 @@ func TestAccept(t *testing.T) { } rlen := binary.BigEndian.Uint16(lbuf) resp := make([]byte, int(rlen)) - n, err = client.Read(resp) + _, err = client.Read(resp) if err != nil { t.Fatal(err) } @@ -854,4 +854,3 @@ func TestServfail(t *testing.T) { t.Errorf("Wrong question: %v", servfail.Questions[0]) } } - diff --git a/intra/rdns/rethinkdns.go b/intra/rdns/rethinkdns.go index 2d5bde0b..de954878 100644 --- a/intra/rdns/rethinkdns.go +++ b/intra/rdns/rethinkdns.go @@ -169,42 +169,39 @@ func (rdns *rethinkdns) blockUnpackedResponse(msg *dns.Msg) (r string, err error return } - cnamed := false - anstype := dns.TypeNone - ansname := "" - // TODO: SVCB/HTTPS tools.ietf.org/html/draft-ietf-dnsop-svcb-https-01 + // handle cname, https/svcb name cloaking: news.ycombinator.com/item?id=26298339 + // adopted from: github.com/DNSCrypt/dnscrypt-proxy/blob/6e8628f79/dnscrypt-proxy/plugin_block_name.go#L178 for _, a := range msg.Answer { - switch a.(type) { + var target string + switch r := a.(type) { case *dns.CNAME: - cnamed = true - case *dns.A: - anstype = dns.TypeA - ansname = a.Header().Name - case *dns.AAAA: - anstype = dns.TypeAAAA - ansname = a.Header().Name + target = r.Target + case *dns.SVCB: + if r.Priority == 0 { + target = r.Target + } + case *dns.HTTPS: + if r.Priority == 0 { + target = r.Target + } default: - // nothing to do + // no-op } - } - if !cnamed || len(ansname) <= 0 { - err = fmt.Errorf("not cnamed") - return - } - if anstype != dns.TypeAAAA && anstype != dns.TypeA { - err = fmt.Errorf("not a or aaaa") - return - } - // err when incoming name != ascii, ignore - ansname, _ = xdns.NormalizeQName(ansname) - block, lists := rdns.trie.DNlookup(ansname, stamp) - // TODO: handle empty lists as err? - if block { - r = strings.Join(rdns.keyToNames(lists), ",") - return + if len(target) <= 0 { + continue + } + + // err when incoming name != ascii, ignore + target, _ = xdns.NormalizeQName(target) + block, lists := rdns.trie.DNlookup(target, stamp) + if block { // TODO: handle empty lists as err? + r = strings.Join(rdns.keyToNames(lists), ",") + return + } } - err = fmt.Errorf("%v cloaked domain not in blocklist %s", ansname, stamp) + + err = fmt.Errorf("answers not in blocklist %s", stamp) return } From 64e20442fd7f693c17e619113f04e10eda9368ea Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Mon, 30 Aug 2021 17:57:51 +0530 Subject: [PATCH 07/17] consolidate various dns transport listeners --- intra/dnscrypt/listener.go | 60 ---------------- intra/dnscrypt/proxy.go | 46 ++++++------ intra/dnsproxy/upstream.go | 50 ++++++------- intra/doh/doh.go | 101 +++++---------------------- intra/doh/doh_test.go | 44 ++++++------ intra/{dnsproxy => rdns}/listener.go | 43 +++++------- intra/rdns/rethinkdns.go | 2 +- intra/tunnel.go | 4 +- 8 files changed, 102 insertions(+), 248 deletions(-) delete mode 100644 intra/dnscrypt/listener.go rename intra/{dnsproxy => rdns}/listener.go (58%) diff --git a/intra/dnscrypt/listener.go b/intra/dnscrypt/listener.go deleted file mode 100644 index 3ca2129c..00000000 --- a/intra/dnscrypt/listener.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2020 RethinkDNS and its authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// This file incorporates work covered by the following copyright and -// permission notice: -// -// ISC License -// -// Copyright (c) 2018-2021 -// Frank Denis - -package dnscrypt - -const ( - // Complete : Transaction completed successfully - Complete = iota - // SendFailed : Failed to send query - SendFailed - // Error : Got no response - Error - // BadQuery : Malformed input - BadQuery - // BadResponse : Response was invalid - BadResponse - // InternalError : This should never happen - InternalError -) - -type dnscryptError struct { - status int - err error -} - -func (e *dnscryptError) Error() string { - return e.err.Error() -} - -func (e *dnscryptError) Unwrap() error { - return e.err -} - -// Summary is a summary of a DNS transaction, reported when it is complete. -type Summary struct { - Latency float64 // Response (or failure) latency in seconds - Query []byte - Response []byte - Server string - RelayServer string - Status int - Blocklists string -} - -// Listener receives Summaries. -type Listener interface { - OnDNSCryptQuery(url string) bool - OnDNSCryptResponse(*Summary) -} diff --git a/intra/dnscrypt/proxy.go b/intra/dnscrypt/proxy.go index 5a67ccac..eaf0585f 100644 --- a/intra/dnscrypt/proxy.go +++ b/intra/dnscrypt/proxy.go @@ -219,7 +219,7 @@ type Proxy struct { registeredRelays []RegisteredServer routes []string quit chan bool - listener Listener + listener rdns.Listener liveServers []string sigterm context.CancelFunc rethinkdns rdns.RethinkDNS @@ -274,7 +274,7 @@ func (proxy *Proxy) prepareForRelay(ip net.IP, port int, encryptedQuery *[]byte) func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blocklists string, serverInfo *ServerInfo, qerr error) { if len(packet) < xdns.MinDNSPacketSize { log.Warnf("DNS query size too short, cannot process dns-crypt query.") - qerr = &dnscryptError{BadQuery, fmt.Errorf("dns-crypt query size too short")} + qerr = &rdns.QueryError{rdns.BadQuery, fmt.Errorf("dns-crypt query size too short")} return } @@ -291,7 +291,7 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl blocklists = state.blocklists if err != nil || saction == ActionDrop { log.Errorf("ActionDrop or err on request %w", err) - qerr = &dnscryptError{BadQuery, err} + qerr = &rdns.QueryError{rdns.BadQuery, err} return } if saction == ActionSynth { @@ -301,7 +301,7 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl // XXX: when the query is blocked and pack-buffer fails // doh falls back to forwarding the query instead. if err != nil { - qerr = &dnscryptError{BadResponse, err} + qerr = &rdns.QueryError{rdns.BadResponse, err} } return } @@ -309,12 +309,12 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl } if len(query) < xdns.MinDNSPacketSize { err = errors.New("dns query size too short, drop dns-crypt query") - qerr = &dnscryptError{BadQuery, err} + qerr = &rdns.QueryError{rdns.BadQuery, err} return } if len(query) > xdns.MaxDNSPacketSize { err = errors.New("dns query size too large, drop dns-crypt query") - qerr = &dnscryptError{BadQuery, err} + qerr = &rdns.QueryError{rdns.BadQuery, err} return } @@ -322,7 +322,7 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl if serverInfo == nil { err = errors.New("server-info nil, drop dns-crypt query") - qerr = &dnscryptError{InternalError, err} + qerr = &rdns.QueryError{rdns.InternalError, err} return } @@ -331,7 +331,7 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl if err != nil { log.Warnf("Encryption failure with dns-crypt query to %s.", serverInfo.String()) - qerr = &dnscryptError{InternalError, err} + qerr = &rdns.QueryError{rdns.InternalError, err} return } @@ -339,23 +339,23 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl if err != nil { log.Warnf("dns crypt query exchange with %s failed: %v", serverInfo.String(), err) - qerr = &dnscryptError{SendFailed, err} + qerr = &rdns.QueryError{rdns.SendFailed, err} return } } else if serverInfo.Proto == stamps.StampProtoTypeDoH { // FIXME: implement log.Errorf("Unsupported dns-crypt transport protocol") - qerr = &dnscryptError{SendFailed, fmt.Errorf("doh not supported with dns-crypt proxy")} + qerr = &rdns.QueryError{rdns.SendFailed, fmt.Errorf("doh not supported with dns-crypt proxy")} return } else { log.Errorf("Unsupported dns-crypt transport protocol") - qerr = &dnscryptError{Error, fmt.Errorf("dns-crypt: unknown protocol")} + qerr = &rdns.QueryError{rdns.NoReponse, fmt.Errorf("dns-crypt: unknown protocol")} return } if len(response) < xdns.MinDNSPacketSize || len(response) > xdns.MaxDNSPacketSize { log.Errorf("response packet size from %s too small or too large", serverInfo.String()) - qerr = &dnscryptError{BadResponse, fmt.Errorf("response packet size too small or too big")} + qerr = &rdns.QueryError{rdns.BadResponse, fmt.Errorf("response packet size too small or too big")} return } @@ -363,7 +363,7 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl if err != nil { log.Errorf("failed to intercept %s response %w", serverInfo.String(), err) - qerr = &dnscryptError{BadResponse, err} + qerr = &rdns.QueryError{rdns.BadResponse, err} } if state.action == ActionSynth && state.response != nil { @@ -372,7 +372,7 @@ func (proxy *Proxy) query(packet []byte, truncate bool) (response []byte, blockl // XXX: when the query is blocked and pack-buffer fails doh falls // back to forwarding the query instead, but here we don't. if err != nil { - qerr = &dnscryptError{BadResponse, err} + qerr = &rdns.QueryError{rdns.BadResponse, err} } return } @@ -395,7 +395,7 @@ func HandleUDP(proxy *Proxy, data []byte) (response []byte, err error) { if proxy.listener != nil { latency := after.Sub(before) - status := Complete + status := rdns.Complete var resolver string var relay string @@ -406,12 +406,12 @@ func HandleUDP(proxy *Proxy, data []byte) (response []byte, err error) { } } - var qerr *dnscryptError + var qerr *rdns.QueryError if errors.As(err, &qerr) { - status = qerr.status + status = qerr.Status } - proxy.listener.OnDNSCryptResponse(&Summary{ + proxy.listener.OnResponse(&rdns.Summary{ Latency: latency.Seconds(), Query: data, Response: response, @@ -465,7 +465,7 @@ func HandleTCP(proxy *Proxy, conn net.Conn) { if proxy.listener != nil { latency := after.Sub(before) - status := Complete + status := rdns.Complete var resolver string var relay string @@ -474,12 +474,12 @@ func HandleTCP(proxy *Proxy, conn net.Conn) { relay = s.RelayTCPAddr.IP.String() } - var qerr *dnscryptError + var qerr *rdns.QueryError if errors.As(err, &qerr) { - status = qerr.status + status = qerr.Status } - proxy.listener.OnDNSCryptResponse(&Summary{ + proxy.listener.OnResponse(&rdns.Summary{ Latency: latency.Seconds(), Query: query, Response: response, @@ -652,7 +652,7 @@ func (proxy *Proxy) AddServers(serverscsv string) (int, error) { } // NewProxy creates a dnscrypt proxy -func NewProxy(l Listener) *Proxy { +func NewProxy(l rdns.Listener) *Proxy { suffixes := critbitgo.NewTrie() for _, line := range undelegatedSet { pattern := xdns.StringReverse(line) diff --git a/intra/dnsproxy/upstream.go b/intra/dnsproxy/upstream.go index 1cc6057c..0820912a 100644 --- a/intra/dnsproxy/upstream.go +++ b/intra/dnsproxy/upstream.go @@ -14,7 +14,6 @@ import ( "io/ioutil" "math" "net" - "net/http" "time" "github.com/celzero/firestack/intra/rdns" @@ -44,12 +43,12 @@ type transport struct { Transport udp *net.UDPAddr tcp *net.TCPAddr - listener Listener + listener rdns.Listener rethinkdns rdns.RethinkDNS } // NewTransport returns a DNS transport, ready for use. -func NewTransport(do *settings.DNSOptions, listener Listener) (t Transport, err error) { +func NewTransport(do *settings.DNSOptions, listener rdns.Listener) (t Transport, err error) { udp, err := net.ResolveUDPAddr("udp", do.IPPort) if err != nil { return @@ -72,9 +71,9 @@ func NewTransport(do *settings.DNSOptions, listener Listener) (t Transport, err // Given a raw DNS query (including the query ID), this function sends the // query. If the query is successful, it returns the response and a nil qerr. Otherwise, // it returns a SERVFAIL response and a qerr with a status value indicating the cause. -func (t *transport) doQuery(network string, q []byte) (response []byte, blocklists string, elapsed time.Duration, qerr *queryError) { +func (t *transport) doQuery(network string, q []byte) (response []byte, blocklists string, elapsed time.Duration, qerr *rdns.QueryError) { if len(q) < 2 { - qerr = &queryError{BadQuery, fmt.Errorf("query length is %d", len(q))} + qerr = &rdns.QueryError{rdns.BadQuery, fmt.Errorf("query length is %d", len(q))} return } @@ -100,7 +99,7 @@ func (t *transport) doQuery(network string, q []byte) (response []byte, blocklis return } -func (t *transport) sendRequest(network string, q []byte) (response []byte, blocklists string, elapsed time.Duration, qerr *queryError) { +func (t *transport) sendRequest(network string, q []byte) (response []byte, blocklists string, elapsed time.Duration, qerr *rdns.QueryError) { var conn net.Conn var dialError error start := time.Now() @@ -122,7 +121,7 @@ func (t *transport) sendRequest(network string, q []byte) (response []byte, bloc } if dialError != nil { elapsed = time.Since(start) - qerr = &queryError{SendFailed, dialError} + qerr = &rdns.QueryError{rdns.SendFailed, dialError} return } @@ -130,7 +129,7 @@ func (t *transport) sendRequest(network string, q []byte) (response []byte, bloc _, err := conn.Write(q) if err != nil { elapsed = time.Since(start) - qerr = &queryError{SendFailed, err} + qerr = &rdns.QueryError{rdns.TransportError, err} return } @@ -138,7 +137,7 @@ func (t *transport) sendRequest(network string, q []byte) (response []byte, bloc response, err = ioutil.ReadAll(conn) elapsed = time.Since(start) if err != nil { - qerr = &queryError{BadResponse, err} + qerr = &rdns.QueryError{rdns.BadResponse, err} return } @@ -149,43 +148,34 @@ func (t *transport) sendRequest(network string, q []byte) (response []byte, bloc response = r // overwrite response when blocked } } else { - qerr = &queryError{BadResponse, fmt.Errorf("response length is %d", len(response))} + qerr = &rdns.QueryError{rdns.BadResponse, fmt.Errorf("response length is %d", len(response))} } return } func (t *transport) Query(network string, q []byte) ([]byte, error) { - var token Token if t.listener != nil { - token = t.listener.OnDNSProxyQuery(t.GetAddr()) + t.listener.OnQuery(t.GetAddr()) } response, blocklists, elapsed, qerr := t.doQuery(network, q) var err error - status := Complete - proxyStatus := http.StatusOK // ? + status := rdns.Complete if qerr != nil { - err = qerr - status = qerr.status - proxyStatus = 0 - - var perr *proxyError - if errors.As(qerr.err, &perr) { - proxyStatus = perr.status - } + err = qerr.Err + status = qerr.Status } if t.listener != nil { - t.listener.OnDNSProxyResponse(token, &Summary{ - Latency: elapsed.Seconds(), - Query: q, - Response: response, - Server: t.GetAddr(), - Status: status, - ProxyStatus: proxyStatus, - Blocklists: blocklists, + t.listener.OnResponse(&rdns.Summary{ + Latency: elapsed.Seconds(), + Query: q, + Response: response, + Server: t.GetAddr(), + Status: status, + Blocklists: blocklists, }) } return response, err diff --git a/intra/doh/doh.go b/intra/doh/doh.go index 22768397..0c1f1fc8 100644 --- a/intra/doh/doh.go +++ b/intra/doh/doh.go @@ -49,46 +49,11 @@ import ( "golang.org/x/net/dns/dnsmessage" ) -const ( - // Complete : Transaction completed successfully - Complete = iota - // SendFailed : Failed to send query - SendFailed - // HTTPError : Got a non-200 HTTP status - HTTPError - // BadQuery : Malformed input - BadQuery - // BadResponse : Response was invalid - BadResponse - // InternalError : This should never happen - InternalError -) - // If the server sends an invalid reply, we start a "servfail hangover" // of this duration, during which all queries are rejected. // This rate-limits queries to misconfigured servers (e.g. wrong URL). const hangoverDuration = 10 * time.Second -// Summary is a summary of a DNS transaction, reported when it is complete. -type Summary struct { - Latency float64 // Response (or failure) latency in seconds - Query []byte - Response []byte - Server string - Status int - HTTPStatus int // Zero unless Status is Complete or HTTPError - Blocklists string // csv separated list of blocklists names, if any. -} - -// A Token is an opaque handle used to match responses to queries. -type Token interface{} - -// Listener receives Summaries. -type Listener interface { - OnQuery(url string) Token - OnResponse(Token, *Summary) -} - // Transport represents a DNS query transport. This interface is exported by gobind, // so it has to be very simple. type Transport interface { @@ -111,7 +76,7 @@ type transport struct { ips ipmap.IPMap client http.Client dialer *net.Dialer - listener Listener + listener rdns.Listener rethinkdns rdns.RethinkDNS hangoverLock sync.RWMutex hangoverExpiration time.Time @@ -169,7 +134,7 @@ func (t *transport) dial(network, addr string) (net.Conn, error) { // timeout but will not mutate it otherwise. // `auth` will provide a client certificate if required by the TLS server. // `listener` will receive the status of each DNS query when it is complete. -func NewTransport(rawurl string, addrs []string, dialer *net.Dialer, auth ClientAuth, listener Listener) (Transport, error) { +func NewTransport(rawurl string, addrs []string, dialer *net.Dialer, auth ClientAuth, listener rdns.Listener) (Transport, error) { if dialer == nil { dialer = &net.Dialer{} } @@ -226,36 +191,15 @@ func NewTransport(rawurl string, addrs []string, dialer *net.Dialer, auth Client return t, nil } -type queryError struct { - status int - err error -} - -func (e *queryError) Error() string { - return e.err.Error() -} - -func (e *queryError) Unwrap() error { - return e.err -} - -type httpError struct { - status int -} - -func (e *httpError) Error() string { - return fmt.Sprintf("HTTP request failed: %d", e.status) -} - // Given a raw DNS query (including the query ID), this function sends the // query. If the query is successful, it returns the response and a nil qerr. Otherwise, // it returns a SERVFAIL response and a qerr with a status value indicating the cause. // Independent of the query's success or failure, this function also returns the // address of the server on a best-effort basis, or nil if the address could not // be determined. -func (t *transport) doQuery(q []byte) (response []byte, blocklists string, server *net.TCPAddr, elapsed time.Duration, qerr *queryError) { +func (t *transport) doQuery(q []byte) (response []byte, blocklists string, server *net.TCPAddr, elapsed time.Duration, qerr *rdns.QueryError) { if len(q) < 2 { - qerr = &queryError{BadQuery, fmt.Errorf("Query length is %d", len(q))} + qerr = &rdns.QueryError{rdns.BadQuery, fmt.Errorf("Query length is %d", len(q))} return } @@ -277,7 +221,7 @@ func (t *transport) doQuery(q []byte) (response []byte, blocklists string, serve t.hangoverLock.RUnlock() if inHangover { response = tryServfail(q) - qerr = &queryError{HTTPError, errors.New("forwarder in servfail hangover")} + qerr = &rdns.QueryError{rdns.TransportError, errors.New("forwarder in servfail hangover")} elapsed = time.Since(start) return } @@ -286,7 +230,7 @@ func (t *transport) doQuery(q []byte) (response []byte, blocklists string, serve q, err := AddEdnsPadding(q) if err != nil { elapsed = time.Since(start) - qerr = &queryError{InternalError, err} + qerr = &rdns.QueryError{rdns.InternalError, err} return } @@ -301,7 +245,7 @@ func (t *transport) doQuery(q []byte) (response []byte, blocklists string, serve binary.BigEndian.PutUint16(q, id) if qerr != nil { // only on send-request errors - if qerr.status != SendFailed { + if qerr.Status != rdns.SendFailed { t.hangoverLock.Lock() t.hangoverExpiration = time.Now().Add(hangoverDuration) t.hangoverLock.Unlock() @@ -316,7 +260,7 @@ func (t *transport) doQuery(q []byte) (response []byte, blocklists string, serve return } -func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname string, server *net.TCPAddr, blocklists string, elapsed time.Duration, qerr *queryError) { +func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname string, server *net.TCPAddr, blocklists string, elapsed time.Duration, qerr *rdns.QueryError) { hostname = t.hostname // The connection used for this request. If the request fails, we will close @@ -345,7 +289,7 @@ func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname req, err := http.NewRequest(http.MethodPost, t.url, bytes.NewBuffer(q)) if err != nil { elapsed = time.Since(start) - qerr = &queryError{InternalError, err} + qerr = &rdns.QueryError{rdns.InternalError, err} return } @@ -417,7 +361,7 @@ func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname if err != nil { elapsed = time.Since(start) - qerr = &queryError{SendFailed, err} + qerr = &rdns.QueryError{rdns.SendFailed, err} return } @@ -426,7 +370,7 @@ func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname elapsed = time.Since(start) if err != nil { - qerr = &queryError{BadResponse, err} + qerr = &rdns.QueryError{rdns.BadResponse, err} return } httpResponse.Body.Close() @@ -442,7 +386,7 @@ func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname httpResponse.Write(respBuf) log.Debugf("%d request: %s\nresponse: %s", id, reqBuf.String(), respBuf.String()) - qerr = &queryError{HTTPError, &httpError{httpResponse.StatusCode}} + qerr = &rdns.QueryError{rdns.TransportError, fmt.Errorf("http-request failed: %d", httpResponse.StatusCode)} return } @@ -456,35 +400,27 @@ func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname response = r } } else { - qerr = &queryError{BadResponse, errors.New("nonzero response-id")} + qerr = &rdns.QueryError{rdns.BadResponse, errors.New("nonzero response-id")} } } else { - qerr = &queryError{BadResponse, fmt.Errorf("response length is %d", len(response))} + qerr = &rdns.QueryError{rdns.BadResponse, fmt.Errorf("response length is %d", len(response))} } return } func (t *transport) Query(q []byte) ([]byte, error) { - var token Token if t.listener != nil { - token = t.listener.OnQuery(t.url) + t.listener.OnQuery(t.url) } response, blocklists, server, elapsed, qerr := t.doQuery(q) var err error - status := Complete - httpStatus := http.StatusOK + status := rdns.Complete if qerr != nil { err = qerr - status = qerr.status - httpStatus = 0 - - var herr *httpError - if errors.As(qerr.err, &herr) { - httpStatus = herr.status - } + status = qerr.Status } if t.listener != nil { @@ -494,13 +430,12 @@ func (t *transport) Query(q []byte) ([]byte, error) { ip = server.IP.String() } - t.listener.OnResponse(token, &Summary{ + t.listener.OnResponse(&rdns.Summary{ Latency: latency.Seconds(), Query: q, Response: response, Server: ip, Status: status, - HTTPStatus: httpStatus, Blocklists: blocklists, }) } diff --git a/intra/doh/doh_test.go b/intra/doh/doh_test.go index a1627c58..733d4800 100644 --- a/intra/doh/doh_test.go +++ b/intra/doh/doh_test.go @@ -36,6 +36,8 @@ import ( "reflect" "testing" + "github.com/celzero/firestack/intra/rdns" + "golang.org/x/net/dns/dnsmessage" ) @@ -157,15 +159,15 @@ func TestBadUrl(t *testing.T) { // Check for failure when the query is too short to be valid. func TestShortQuery(t *testing.T) { - var qerr *queryError + var qerr *rdns.QueryError doh, _ := NewTransport(testURL, ips, nil, nil, nil) _, err := doh.Query([]byte{}) if err == nil { t.Error("Empty query should fail") } else if !errors.As(err, &qerr) { t.Errorf("Wrong error type: %v", err) - } else if qerr.status != BadQuery { - t.Errorf("Wrong error status: %d", qerr.status) + } else if qerr.Status != rdns.BadQuery { + t.Errorf("Wrong error status: %d", qerr.Status) } _, err = doh.Query([]byte{1}) @@ -173,8 +175,8 @@ func TestShortQuery(t *testing.T) { t.Error("One byte query should fail") } else if !errors.As(err, &qerr) { t.Errorf("Wrong error type: %v", err) - } else if qerr.status != BadQuery { - t.Errorf("Wrong error status: %d", qerr.status) + } else if qerr.Status != rdns.BadQuery { + t.Errorf("Wrong error status: %d", qerr.Status) } } @@ -354,13 +356,13 @@ func TestEmptyResponse(t *testing.T) { }() _, err := doh.Query(simpleQueryBytes) - var qerr *queryError + var qerr *rdns.QueryError if err == nil { t.Error("Empty body should cause an error") } else if !errors.As(err, &qerr) { t.Errorf("Wrong error type: %v", err) - } else if qerr.status != BadResponse { - t.Errorf("Wrong error status: %d", qerr.status) + } else if qerr.Status != rdns.BadResponse { + t.Errorf("Wrong error status: %d", qerr.Status) } } @@ -384,13 +386,13 @@ func TestHTTPError(t *testing.T) { }() _, err := doh.Query(simpleQueryBytes) - var qerr *queryError + var qerr *rdns.QueryError if err == nil { t.Error("Empty body should cause an error") } else if !errors.As(err, &qerr) { t.Errorf("Wrong error type: %v", err) - } else if qerr.status != HTTPError { - t.Errorf("Wrong error status: %d", qerr.status) + } else if qerr.Status != rdns.TransportError { + t.Errorf("Wrong error status: %d", qerr.Status) } } @@ -403,29 +405,29 @@ func TestSendFailed(t *testing.T) { rt.err = errors.New("test") _, err := doh.Query(simpleQueryBytes) - var qerr *queryError + var qerr *rdns.QueryError if err == nil { t.Error("Send failure should be reported") } else if !errors.As(err, &qerr) { t.Errorf("Wrong error type: %v", err) - } else if qerr.status != SendFailed { - t.Errorf("Wrong error status: %d", qerr.status) + } else if qerr.Status != rdns.SendFailed { + t.Errorf("Wrong error status: %d", qerr.Status) } else if !errors.Is(qerr, rt.err) { t.Errorf("Underlying error is not retained") } } type fakeListener struct { - Listener - summary *Summary + rdns.Listener + summary *rdns.Summary } -func (l *fakeListener) OnQuery(url string) Token { - return nil +func (l *fakeListener) OnQuery(domain string) string { + return "" } -func (l *fakeListener) OnResponse(tok Token, summ *Summary) { - l.summary = summ +func (l *fakeListener) OnResponse(s *rdns.Summary) { + l.summary = s } type fakeConn struct { @@ -479,7 +481,7 @@ func TestListener(t *testing.T) { if s.Server != "192.0.2.2" { t.Errorf("Wrong server IP string: %s", s.Server) } - if s.Status != Complete { + if s.Status != rdns.Complete { t.Errorf("Wrong status: %d", s.Status) } } diff --git a/intra/dnsproxy/listener.go b/intra/rdns/listener.go similarity index 58% rename from intra/dnsproxy/listener.go rename to intra/rdns/listener.go index 76f7d951..e80fa2a9 100644 --- a/intra/dnsproxy/listener.go +++ b/intra/rdns/listener.go @@ -4,44 +4,36 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -package dnsproxy - -import "fmt" +package rdns const ( // Complete : Transaction completed successfully Complete = iota // SendFailed : Failed to send query SendFailed - // ProxyError : Got an error from upstream - ProxyError + // NoReponse : Got no response + NoReponse // BadQuery : Malformed input BadQuery // BadResponse : Response was invalid BadResponse // InternalError : This should never happen InternalError + // TransportError: Transport has issues + TransportError ) -type queryError struct { - status int - err error -} - -func (e *queryError) Error() string { - return e.err.Error() +type QueryError struct { + Status int + Err error } -func (e *queryError) Unwrap() error { - return e.err +func (e *QueryError) Error() string { + return e.Err.Error() } -type proxyError struct { - status int -} - -func (e *proxyError) Error() string { - return fmt.Sprintf("proxy request fail: %d", e.status) +func (e *QueryError) Unwrap() error { + return e.Err } // Summary is a summary of a DNS transaction, reported when it is complete. @@ -50,16 +42,13 @@ type Summary struct { Query []byte Response []byte Server string - Status int - ProxyStatus int // Zero unless Status is Complete or ProxyError + RelayServer string + Status int // Zero unless Status is Complete or ProxyError Blocklists string // csv separated list of blocklists names, if any. } -// A Token is an opaque handle used to match responses to queries. -type Token interface{} - // Listener receives Summaries. type Listener interface { - OnDNSProxyQuery(ipport string) Token - OnDNSProxyResponse(Token, *Summary) + OnQuery(domain string) string + OnResponse(*Summary) } diff --git a/intra/rdns/rethinkdns.go b/intra/rdns/rethinkdns.go index de954878..53cbb4ee 100644 --- a/intra/rdns/rethinkdns.go +++ b/intra/rdns/rethinkdns.go @@ -26,7 +26,7 @@ import ( ) const ( - blocklistHeaderKey = "x-nile-flags" // "x-bl-fl" + blocklistHeaderKey = "x-nile-flags" localBlock = 0 remoteBlock = 1 ) diff --git a/intra/tunnel.go b/intra/tunnel.go index c41b83ce..fe21b2be 100644 --- a/intra/tunnel.go +++ b/intra/tunnel.go @@ -46,9 +46,7 @@ import ( type Listener interface { UDPListener TCPListener - doh.Listener - dnscrypt.Listener - dnsproxy.Listener + rdns.Listener } // Tunnel represents an Intra session. From 762b081df332b16714eb9ba9e24ecdf4d9e3a6ec Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Fri, 17 Sep 2021 17:36:26 +0530 Subject: [PATCH 08/17] bye shadowsocks; hello go1.17 --- Makefile | 9 +- go.mod | 9 +- go.sum | 186 +--------------------------- make-aar | 4 +- outline/android/tun2socks.go | 67 ---------- outline/apple/Info.plist | 10 -- outline/apple/tun2socks.go | 70 ----------- outline/electron/main.go | 163 ------------------------ outline/shadowsocks/connectivity.go | 78 ------------ outline/tunnel.go | 111 ----------------- shadowsocks/connectivity.go | 106 ---------------- shadowsocks/connectivity_test.go | 133 -------------------- shadowsocks/tcp.go | 37 ------ shadowsocks/udp.go | 95 -------------- 14 files changed, 10 insertions(+), 1068 deletions(-) delete mode 100644 outline/android/tun2socks.go delete mode 100644 outline/apple/Info.plist delete mode 100644 outline/apple/tun2socks.go delete mode 100644 outline/electron/main.go delete mode 100644 outline/shadowsocks/connectivity.go delete mode 100644 outline/tunnel.go delete mode 100644 shadowsocks/connectivity.go delete mode 100644 shadowsocks/connectivity_test.go delete mode 100644 shadowsocks/tcp.go delete mode 100644 shadowsocks/udp.go diff --git a/Makefile b/Makefile index b304c904..b242e5bc 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,6 @@ WINDOWS_BUILDDIR=$(BUILDDIR)/windows LINUX_BUILDDIR=$(BUILDDIR)/linux ANDROID_BUILD_CMD="$(GOBIND) -a -ldflags $(ANDROID_LDFLAGS) -target=android -tags android -work -o $(ANDROID_ARTIFACT)" -ANDROID_OUTLINE_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/outline/android $(IMPORT_PATH)/outline/shadowsocks" ANDROID_INTRA_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/intra $(IMPORT_PATH)/intra/android $(IMPORT_PATH)/intra/doh $(IMPORT_PATH)/intra/split $(IMPORT_PATH)/intra/protect $(IMPORT_PATH)/intra/settings $(IMPORT_PATH)/intra/dnscrypt $(IMPORT_PATH)/intra/dnsproxy $(IMPORT_PATH)/intra/rdns $(IMPORT_PATH)/intra/xdns" IOS_BUILD_CMD="$(GOBIND) -a -ldflags $(LDFLAGS) -bundleid org.outline.tun2socks -target=ios/arm64 -tags ios -o $(IOS_ARTIFACT) $(IMPORT_PATH)/outline/apple $(IMPORT_PATH)/outline/shadowsocks" MACOS_BUILD_CMD="./tools/$(GOBIND) -a -ldflags $(LDFLAGS) -bundleid org.outline.tun2socks -target=ios/amd64 -tags ios -o $(MACOS_ARTIFACT) $(IMPORT_PATH)/outline/apple $(IMPORT_PATH)/outline/shadowsocks" @@ -28,15 +27,13 @@ LINUX_BUILD_CMD="$(XGOCMD) -ldflags $(XGO_LDFLAGS) --targets=linux/amd64 -dest $ define build mkdir -p $(1) + go env -w GOFLAGS=-mod=mod # github.com/golang/go/issues/44129 eval $(2) endef -.PHONY: android-outline android-intra ios linux macos windows clean +.PHONY: android-intra ios linux macos windows clean -all: android-outline android-intra ios linux macos windows - -android-outline: - $(call build,$(ANDROID_BUILDDIR),$(ANDROID_OUTLINE_BUILD_CMD)) +all: android-intra ios linux macos windows android-intra: $(call build,$(ANDROID_BUILDDIR),$(ANDROID_INTRA_BUILD_CMD)) diff --git a/go.mod b/go.mod index 32ee19f2..d22c9f23 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,6 @@ go 1.15 require ( github.com/Jigsaw-Code/getsni v1.0.0 - github.com/Jigsaw-Code/outline-ss-server v1.3.5 - github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect - github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect github.com/celzero/gotrie v0.0.0-20210413153406-d9d0dcea9cbd github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 github.com/eycorsican/go-tun2socks v1.16.11 @@ -15,10 +12,12 @@ require ( github.com/jedisct1/xsecretbox v0.0.0-20210813171751-8f930e127d47 github.com/k-sone/critbitgo v1.4.0 github.com/miekg/dns v1.1.43 - github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect golang.org/x/net v0.0.0-20210825183410-e898025ed96a golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf + golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect + github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + ) diff --git a/go.sum b/go.sum index 6ca44b04..40ffea67 100644 --- a/go.sum +++ b/go.sum @@ -1,151 +1,31 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Jigsaw-Code/choir v1.0.1 h1:WeRt6aTn5L+MtRNqRJ+J1RKgoO8CyXXt1dtZghy2KjE= -github.com/Jigsaw-Code/choir v1.0.1/go.mod h1:c4Wd1y1PeCajZbKZV+ZmcFGMDoduyqMCEMHW5iqzWXI= -github.com/Jigsaw-Code/getsni v0.0.0-20190807203514-efe2dbf35d1f h1:PT61aMvdZPh/8L5FjmKu8DS4/VnmwSJICVZ/THpmLF0= -github.com/Jigsaw-Code/getsni v0.0.0-20190807203514-efe2dbf35d1f/go.mod h1:C68VBkZJR/wcvgo6pmdlm6snMHWiLE844lXJ028Qh8Y= github.com/Jigsaw-Code/getsni v1.0.0 h1:OUTIu7wTBi/7DMX+RkZrN7XhU3UDevTEsAWK4gsqSwE= github.com/Jigsaw-Code/getsni v1.0.0/go.mod h1:Ps0Ec3fVMKLyAItVbMKoQFq1lDjtFQXZ+G5nRNNh/QE= -github.com/Jigsaw-Code/outline-ss-server v1.3.2 h1:hZW1wSNiD0Nqp5gRL0avqQqOvqZsHhG6V9Ac0zWeCU0= -github.com/Jigsaw-Code/outline-ss-server v1.3.2/go.mod h1:eXiKkyLq4AqQvpTvDL/rYUv30OeS+lxF+fQaGGKnzmY= -github.com/Jigsaw-Code/outline-ss-server v1.3.5 h1:XbrcJ9EP9qklr9eEi/Mp+UMMT7GhXgmvfxL5CjtS1DA= -github.com/Jigsaw-Code/outline-ss-server v1.3.5/go.mod h1:eXiKkyLq4AqQvpTvDL/rYUv30OeS+lxF+fQaGGKnzmY= -github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= -github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw= github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/celzero/gotrie v0.0.0-20201019185855-82d11a96233f h1:75CoIjHTjfEY7hwLXGYm5A9LDGkSp/AIJLb9fJ04LxM= -github.com/celzero/gotrie v0.0.0-20201019185855-82d11a96233f/go.mod h1:Qo0txkBFM3m4+mXbyY6Pd46jCEUUHRd5C3Y4cSdA7jM= github.com/celzero/gotrie v0.0.0-20210413153406-d9d0dcea9cbd h1:hKeS1WzcKkGk/NfOIgTHHFhYgSvKpUEDTtg21VYbPVQ= github.com/celzero/gotrie v0.0.0-20210413153406-d9d0dcea9cbd/go.mod h1:Qo0txkBFM3m4+mXbyY6Pd46jCEUUHRd5C3Y4cSdA7jM= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 h1:lS3P5Nw3oPO05Lk2gFiYUOL3QPaH+fRoI1wFOc4G1UY= github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM= github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/eycorsican/go-tun2socks v1.16.11 h1:+hJDNgisrYaGEqoSxhdikMgMJ4Ilfwm/IZDrWRrbaH8= github.com/eycorsican/go-tun2socks v1.16.11/go.mod h1:wgB2BFT8ZaPKyKOQ/5dljMG/YIow+AIXyq4KBwJ5sGQ= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c h1:a/NQUT7AXkEfhaZ+nb7Uzqijo1Qc7C7SZpRrv+6UQDA= -github.com/jedisct1/go-clocksmith v0.0.0-20190707124905-73e087c7979c/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw= github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868 h1:QZ79mRbNwYYYmiVjyv+X0NKgYE6nyN1yo3gtEFdzpiE= github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw= -github.com/jedisct1/go-dnsstamps v0.0.0-20200621175006-302248eecc94 h1:O5X61fl3p/dl+7hLDwDamJxRY6z/LwuH1XD+OyNNlxE= -github.com/jedisct1/go-dnsstamps v0.0.0-20200621175006-302248eecc94/go.mod h1:128Ik0lG+DBYL6zaSgN3icmzDASeQgkSy3+Sp10trLc= github.com/jedisct1/go-dnsstamps v0.0.0-20210810213811-61cc83d2a354 h1:sIB9mDh2spQdh95jeXF2h9uSNtObbehD0YbDCzmqbM8= github.com/jedisct1/go-dnsstamps v0.0.0-20210810213811-61cc83d2a354/go.mod h1:t35n6rsPE3nD3RXbc5hI5Ax1ci/SSYTpx0BdMXh/1aE= -github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9 h1:nGfB2s9K0GyHuNkJmXkIjP+m7je6Q6gjirr+weAEtDo= -github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9/go.mod h1:MipBKo+gZlzpd1JXA1OliuwvtQizlFeu4aMAyTLh8bo= github.com/jedisct1/xsecretbox v0.0.0-20210813171751-8f930e127d47 h1:Ow0DNq/gvOiJBbOE2xY5beyNK0pKtcpsjjD60SL+9nE= github.com/jedisct1/xsecretbox v0.0.0-20210813171751-8f930e127d47/go.mod h1:FVoMcE35bRG6gw8u8b5OzQIySe/TfZfHRR8Fno/s1d8= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k-sone/critbitgo v1.4.0 h1:l71cTyBGeh6X5ATh6Fibgw3+rtNT80BA0uNNWgkPrbE= github.com/k-sone/critbitgo v1.4.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OAp/UQvo0s= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= -github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/oschwald/geoip2-golang v1.4.0 h1:5RlrjCgRyIGDz/mBmPfnAF4h8k0IAcRv9PvrpOfz+Ug= -github.com/oschwald/geoip2-golang v1.4.0/go.mod h1:8QwxJvRImBH+Zl6Aa6MaIcs5YdlZSTKtzmPGzQqi9ng= -github.com/oschwald/maxminddb-golang v1.6.0 h1:KAJSjdHQ8Kv45nFIbtoLGrGWqHFajOIm7skTyz/+Dls= -github.com/oschwald/maxminddb-golang v1.6.0/go.mod h1:DUJFucBg2cvqx42YmDa/+xHvb0elJtOm3o4aFQ/nb/w= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= -github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= -github.com/shadowsocks/go-shadowsocks2 v0.1.4-0.20201002022019-75d43273f5a5 h1:PH+QJxWqlFTux7T1inBImjualkfKum8UKgAsRqDMmbM= -github.com/shadowsocks/go-shadowsocks2 v0.1.4-0.20201002022019-75d43273f5a5/go.mod h1:/jk7XQoEyq98sd0ckJtBhaaFqfnzWm7CX/OzUAIy/Kk= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/songgao/water v0.0.0-20190725173103-fd331bda3f4b/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= -github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= -github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf/go.mod h1:CLUSJbazqETbaR+i0YAhXBICV9TrKH93pziccMhmhpM= github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da h1:7x8pJcBTdKTBpQbRjZZc9o6CDquXBbvm9UIrR6ZSRJ4= @@ -153,18 +33,10 @@ github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da/go.mod h1:7NloQc github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A= github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210812204632-0ba0e8f03122 h1:AOT7vJYHE32m61R8d1WlcqhOO1AocesDsKpcMq+UOaA= golang.org/x/crypto v0.0.0-20210812204632-0ba0e8f03122/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -172,60 +44,30 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 h1:h+GZ3ubjuWaQjGe8owMGcmMVCqs0xYJtRG5y2bpHaqU= -golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU= golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw= golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190909082730-f460065e899a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201007165808-a893ed343c85 h1:v7tXcN5Dmvk08x9LWujjDQbk/26sd3IqhKa1NfaKmpM= -golang.org/x/sys v0.0.0-20201007165808-a893ed343c85/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -236,35 +78,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/make-aar b/make-aar index 3e92a180..f086a8ad 100755 --- a/make-aar +++ b/make-aar @@ -18,8 +18,8 @@ ls -ltr $ANDROID_HOME/platforms/ ls -ltr $ANDROID_HOME/ndk/ # download golang -curl -Lso go.tar.gz https://golang.org/dl/go1.15.4.linux-amd64.tar.gz -echo "eb61005f0b932c93b424a3a4eaa67d72196c79129d9a3ea8578047683e2c80d5 go.tar.gz" | sha256sum -c - +curl -Lso go.tar.gz https://golang.org/dl/go1.17.1.linux-amd64.tar.gz +echo "dab7d9c34361dc21ec237d584590d72500652e7c909bf082758fb63064fca0ef go.tar.gz" | sha256sum -c - # setup golang mkdir -p golang diff --git a/outline/android/tun2socks.go b/outline/android/tun2socks.go deleted file mode 100644 index 75193dec..00000000 --- a/outline/android/tun2socks.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019 The Outline Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tun2socks - -import ( - "fmt" - "math" - "runtime/debug" - - "github.com/celzero/firestack/outline" - "github.com/celzero/firestack/tunnel" - "github.com/eycorsican/go-tun2socks/common/log" -) - -func init() { - // Conserve memory by increasing garbage collection frequency. - debug.SetGCPercent(10) - log.SetLevel(log.WARN) -} - -// OutlineTunnel embeds the tun2socks.OutlineTunnel interface so it gets exported by gobind. -type OutlineTunnel interface { - outline.Tunnel -} - -// ConnectShadowsocksTunnel reads packets from a TUN device and routes it to a Shadowsocks proxy server. -// Returns an OutlineTunnel instance and does *not* take ownership of the TUN file descriptor; the -// caller is responsible for closing after OutlineTunnel disconnects. -// -// `fd` is the TUN device. The OutlineTunnel acquires an additional reference to it, which -// is released by OutlineTunnel.Disconnect(), so the caller must close `fd` _and_ call -// Disconnect() in order to close the TUN device. -// `host` is IP address of the Shadowsocks proxy server. -// `port` is the port of the Shadowsocks proxy server. -// `password` is the password of the Shadowsocks proxy. -// `cipher` is the encryption cipher the Shadowsocks proxy. -// `isUDPEnabled` indicates whether the tunnel and/or network enable UDP proxying. -// -// Throws an exception if the TUN file descriptor cannot be opened, or if the tunnel fails to -// connect. -func ConnectShadowsocksTunnel(fd int, host string, port int, password, cipher string, isUDPEnabled bool) (OutlineTunnel, error) { - if port <= 0 || port > math.MaxUint16 { - return nil, fmt.Errorf("Invalid port number: %v", port) - } - tun, err := tunnel.MakeTunFile(fd) - if err != nil { - return nil, err - } - t, err := outline.NewTunnel(host, port, password, cipher, isUDPEnabled, tun) - if err != nil { - return nil, err - } - go tunnel.ProcessInputPackets(t, tun) - return t, nil -} diff --git a/outline/apple/Info.plist b/outline/apple/Info.plist deleted file mode 100644 index b806cc5a..00000000 --- a/outline/apple/Info.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - CFBundleExecutable - Tun2socks - CFBundleIdentifier - org.outline.tun2socks - - diff --git a/outline/apple/tun2socks.go b/outline/apple/tun2socks.go deleted file mode 100644 index 81f1feff..00000000 --- a/outline/apple/tun2socks.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019 The Outline Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tun2socks - -import ( - "errors" - "fmt" - "io" - "math" - "runtime/debug" - "time" - - "github.com/celzero/firestack/outline" -) - -// OutlineTunnel embeds the tun2socks.Tunnel interface so it gets exported by gobind. -type OutlineTunnel interface { - outline.Tunnel -} - -// TunWriter is an interface that allows for outputting packets to the TUN (VPN). -type TunWriter interface { - io.WriteCloser -} - -func init() { - // Apple VPN extensions have a memory limit of 15MB. Conserve memory by increasing garbage - // collection frequency and returning memory to the OS every minute. - debug.SetGCPercent(10) - // TODO: Check if this is still needed in go 1.13, which returns memory to the OS - // automatically. - ticker := time.NewTicker(time.Minute * 1) - go func() { - for range ticker.C { - debug.FreeOSMemory() - } - }() -} - -// ConnectShadowsocksTunnel reads packets from a TUN device and routes it to a Shadowsocks proxy server. -// Returns an OutlineTunnel instance that should be used to input packets to the tunnel. -// -// `tunWriter` is used to output packets to the TUN (VPN). -// `host` is IP address of the Shadowsocks proxy server. -// `port` is the port of the Shadowsocks proxy server. -// `password` is the password of the Shadowsocks proxy. -// `cipher` is the encryption cipher the Shadowsocks proxy. -// `isUDPEnabled` indicates whether the tunnel and/or network enable UDP proxying. -// -// Sets an error if the tunnel fails to connect. -func ConnectShadowsocksTunnel(tunWriter TunWriter, host string, port int, password, cipher string, isUDPEnabled bool) (OutlineTunnel, error) { - if tunWriter == nil { - return nil, errors.New("Must provide a TunWriter") - } else if port <= 0 || port > math.MaxUint16 { - return nil, fmt.Errorf("Invalid port number: %v", port) - } - return outline.NewTunnel(host, port, password, cipher, isUDPEnabled, tunWriter) -} diff --git a/outline/electron/main.go b/outline/electron/main.go deleted file mode 100644 index f924515c..00000000 --- a/outline/electron/main.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2019 The Outline Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "flag" - "fmt" - "io" - "os" - "os/signal" - "strings" - "syscall" - "time" - - oss "github.com/celzero/firestack/outline/shadowsocks" - "github.com/celzero/firestack/shadowsocks" - "github.com/eycorsican/go-tun2socks/common/log" - _ "github.com/eycorsican/go-tun2socks/common/log/simple" // Register a simple logger. - "github.com/eycorsican/go-tun2socks/core" - "github.com/eycorsican/go-tun2socks/proxy/dnsfallback" - "github.com/eycorsican/go-tun2socks/tun" -) - -const ( - mtu = 1500 - udpTimeout = 30 * time.Second - persistTun = true // Linux: persist the TUN interface after the last open file descriptor is closed. -) - -var args struct { - tunAddr *string - tunGw *string - tunMask *string - tunName *string - tunDNS *string - proxyHost *string - proxyPort *int - proxyPassword *string - proxyCipher *string - logLevel *string - checkConnectivity *bool - dnsFallback *bool - version *bool -} -var version string // Populated at build time through `-X main.version=...` -var lwipWriter io.Writer - -func main() { - args.tunAddr = flag.String("tunAddr", "10.0.85.2", "TUN interface IP address") - args.tunGw = flag.String("tunGw", "10.0.85.1", "TUN interface gateway") - args.tunMask = flag.String("tunMask", "255.255.255.0", "TUN interface network mask; prefixlen for IPv6") - args.tunDNS = flag.String("tunDNS", "1.1.1.1,9.9.9.9,208.67.222.222", "Comma-separated list of DNS resolvers for the TUN interface (Windows only)") - args.tunName = flag.String("tunName", "tun0", "TUN interface name") - args.proxyHost = flag.String("proxyHost", "", "Shadowsocks proxy hostname or IP address") - args.proxyPort = flag.Int("proxyPort", 0, "Shadowsocks proxy port number") - args.proxyPassword = flag.String("proxyPassword", "", "Shadowsocks proxy password") - args.proxyCipher = flag.String("proxyCipher", "chacha20-ietf-poly1305", "Shadowsocks proxy encryption cipher") - args.logLevel = flag.String("logLevel", "info", "Logging level: debug|info|warn|error|none") - args.dnsFallback = flag.Bool("dnsFallback", false, "Enable DNS fallback over TCP (overrides the UDP handler).") - args.checkConnectivity = flag.Bool("checkConnectivity", false, "Check the proxy TCP and UDP connectivity and exit.") - args.version = flag.Bool("version", false, "Print the version and exit.") - - flag.Parse() - - if *args.version { - fmt.Println(version) - os.Exit(0) - } - - setLogLevel(*args.logLevel) - - // Validate proxy flags - if *args.proxyHost == "" { - log.Errorf("Must provide a Shadowsocks proxy host name or IP address") - os.Exit(oss.IllegalConfiguration) - } else if *args.proxyPort <= 0 || *args.proxyPort > 65535 { - log.Errorf("Must provide a valid Shadowsocks proxy port [1:65535]") - os.Exit(oss.IllegalConfiguration) - } else if *args.proxyPassword == "" { - log.Errorf("Must provide a Shadowsocks proxy password") - os.Exit(oss.IllegalConfiguration) - } else if *args.proxyCipher == "" { - log.Errorf("Must provide a Shadowsocks proxy encryption cipher") - os.Exit(oss.IllegalConfiguration) - } - - if *args.checkConnectivity { - connErrCode, err := oss.CheckConnectivity(*args.proxyHost, *args.proxyPort, *args.proxyPassword, *args.proxyCipher) - log.Debugf("Connectivity checks error code: %v", connErrCode) - if err != nil { - log.Errorf("Failed to perform connectivity checks: %v", err) - } - os.Exit(connErrCode) - } - - // Open TUN device - dnsResolvers := strings.Split(*args.tunDNS, ",") - tunDevice, err := tun.OpenTunDevice(*args.tunName, *args.tunAddr, *args.tunGw, *args.tunMask, dnsResolvers, persistTun) - if err != nil { - log.Errorf("Failed to open TUN device: %v", err) - os.Exit(oss.SystemMisconfigured) - } - // Output packets to TUN device - core.RegisterOutputFn(tunDevice.Write) - - // Register TCP and UDP connection handlers - core.RegisterTCPConnHandler( - shadowsocks.NewTCPHandler(*args.proxyHost, *args.proxyPort, *args.proxyPassword, *args.proxyCipher)) - if *args.dnsFallback { - // UDP connectivity not supported, fall back to DNS over TCP. - log.Debugf("Registering DNS fallback UDP handler") - core.RegisterUDPConnHandler(dnsfallback.NewUDPHandler()) - } else { - core.RegisterUDPConnHandler( - shadowsocks.NewUDPHandler(*args.proxyHost, *args.proxyPort, *args.proxyPassword, *args.proxyCipher, udpTimeout)) - } - - // Configure LWIP stack to receive input data from the TUN device - lwipWriter := core.NewLWIPStack() - go func() { - _, err := io.CopyBuffer(lwipWriter, tunDevice, make([]byte, mtu)) - if err != nil { - log.Errorf("Failed to write data to network stack: %v", err) - os.Exit(oss.Unexpected) - } - }() - - log.Infof("tun2socks running...") - - osSignals := make(chan os.Signal, 1) - signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGHUP) - sig := <-osSignals - log.Debugf("Received signal: %v", sig) -} - -func setLogLevel(level string) { - switch strings.ToLower(level) { - case "debug": - log.SetLevel(log.DEBUG) - case "info": - log.SetLevel(log.INFO) - case "warn": - log.SetLevel(log.WARN) - case "error": - log.SetLevel(log.ERROR) - case "none": - log.SetLevel(log.NONE) - default: - log.SetLevel(log.INFO) - } -} diff --git a/outline/shadowsocks/connectivity.go b/outline/shadowsocks/connectivity.go deleted file mode 100644 index 109677bf..00000000 --- a/outline/shadowsocks/connectivity.go +++ /dev/null @@ -1,78 +0,0 @@ -package shadowsocks - -import ( - "net" - "strconv" - "time" - - oss "github.com/celzero/firestack/shadowsocks" - shadowsocks "github.com/Jigsaw-Code/outline-ss-server/client" -) - -// Outline error codes. Must be kept in sync with definitions in outline-client/cordova-plugin-outline/outlinePlugin.js -const ( - NoError = 0 - Unexpected = 1 - NoVPNPermissions = 2 - AuthenticationFailure = 3 - UDPConnectivity = 4 - Unreachable = 5 - VpnStartFailure = 6 - IllegalConfiguration = 7 - ShadowsocksStartFailure = 8 - ConfigureSystemProxyFailure = 9 - NoAdminPermissions = 10 - UnsupportedRoutingTable = 11 - SystemMisconfigured = 12 -) - -const reachabilityTimeout = 10 * time.Second - -// CheckConnectivity determines whether the Shadowsocks proxy can relay TCP and UDP traffic under -// the current network. Parallelizes the execution of TCP and UDP checks, selects the appropriate -// error code to return accounting for transient network failures. -// Returns an error if an unexpected error ocurrs. -func CheckConnectivity(host string, port int, password, cipher string) (int, error) { - client, err := shadowsocks.NewClient(host, port, password, cipher) - if err != nil { - // TODO: Inspect error for invalid cipher error or proxy host resolution failure. - return Unexpected, err - } - tcpChan := make(chan error) - // Check whether the proxy is reachable and that the client is able to authenticate to the proxy - go func() { - tcpChan <- oss.CheckTCPConnectivityWithHTTP(client, "http://example.com") - }() - // Check whether UDP is supported - udpErr := oss.CheckUDPConnectivityWithDNS(client, shadowsocks.NewAddr("1.1.1.1:53", "udp")) - if udpErr == nil { - // The UDP connectvity check is a superset of the TCP checks. If the other tests fail, - // assume it's due to intermittent network conditions and declare success anyway. - return NoError, nil - } - tcpErr := <-tcpChan - if tcpErr == nil { - // The TCP connectivity checks succeeded, which means UDP is not supported. - return UDPConnectivity, nil - } - _, isReachabilityError := tcpErr.(*oss.ReachabilityError) - _, isAuthError := tcpErr.(*oss.AuthenticationError) - if isAuthError { - return AuthenticationFailure, nil - } else if isReachabilityError { - return Unreachable, nil - } - // The error is not related to the connectivity checks. - return Unexpected, tcpErr -} - -// CheckServerReachable determines whether the server at `host:port` is reachable over TCP. -// Returns an error if the server is unreachable. -func CheckServerReachable(host string, port int) error { - conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, strconv.Itoa(port)), reachabilityTimeout) - if err != nil { - return err - } - conn.Close() - return nil -} diff --git a/outline/tunnel.go b/outline/tunnel.go deleted file mode 100644 index 23f87f00..00000000 --- a/outline/tunnel.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2020 RethinkDNS and its authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// This file incorporates work covered by the following copyright and -// permission notice: -// -// Copyright 2019 The Outline Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package outline - -import ( - "errors" - "fmt" - "io" - "time" - - "github.com/eycorsican/go-tun2socks/core" - "github.com/eycorsican/go-tun2socks/proxy/dnsfallback" - - oss "github.com/celzero/firestack/shadowsocks" - "github.com/celzero/firestack/tunnel" - shadowsocks "github.com/Jigsaw-Code/outline-ss-server/client" -) - -// Tunnel represents a tunnel from a TUN device to a server. -type Tunnel interface { - tunnel.Tunnel - - // UpdateUDPSupport determines if UDP is supported following a network connectivity change. - // Sets the tunnel's UDP connection handler accordingly, falling back to DNS over TCP if UDP is not supported. - // Returns whether UDP proxying is supported in the new network. - UpdateUDPSupport() bool -} - -type outlinetunnel struct { - tunnel.Tunnel - lwipStack core.LWIPStack - host string - port int - password string - cipher string - isUDPEnabled bool // Whether the tunnel supports proxying UDP. -} - -// NewTunnel connects a tunnel to a Shadowsocks proxy server and returns an `outline.Tunnel`. -// -// `host` is the IP or domain of the Shadowsocks proxy. -// `port` is the port of the Shadowsocks proxy. -// `password` is the password of the Shadowsocks proxy. -// `cipher` is the encryption cipher used by the Shadowsocks proxy. -// `isUDPEnabled` indicates if the Shadowsocks proxy and the network support proxying UDP traffic. -// `tunWriter` is used to output packets back to the TUN device. OutlineTunnel.Disconnect() will close `tunWriter`. -func NewTunnel(host string, port int, password, cipher string, isUDPEnabled bool, tunWriter io.WriteCloser) (Tunnel, error) { - if tunWriter == nil { - return nil, errors.New("Must provide a TUN writer") - } - _, err := shadowsocks.NewClient(host, port, password, cipher) - if err != nil { - return nil, fmt.Errorf("Invalid Shadowsocks proxy parameters: %v", err.Error()) - } - core.RegisterOutputFn(func(data []byte) (int, error) { - return tunWriter.Write(data) - }) - lwipStack := core.NewLWIPStack() - base := tunnel.NewTunnel(tunWriter, lwipStack) - t := &outlinetunnel{base, lwipStack, host, port, password, cipher, isUDPEnabled} - t.registerConnectionHandlers() - return t, nil -} - -func (t *outlinetunnel) UpdateUDPSupport() bool { - client, err := shadowsocks.NewClient(t.host, t.port, t.password, t.cipher) - if err != nil { - return false - } - isUDPEnabled := oss.CheckUDPConnectivityWithDNS(client, shadowsocks.NewAddr("1.1.1.1:53", "udp")) == nil - if t.isUDPEnabled != isUDPEnabled { - t.isUDPEnabled = isUDPEnabled - t.lwipStack.Close() // Close existing connections to avoid using the previous handlers. - t.registerConnectionHandlers() - } - return isUDPEnabled -} - -// Registers UDP and TCP Shadowsocks connection handlers to the tunnel's host and port. -// Registers a DNS/TCP fallback UDP handler when UDP is disabled. -func (t *outlinetunnel) registerConnectionHandlers() { - var udpHandler core.UDPConnHandler - if t.isUDPEnabled { - udpHandler = oss.NewUDPHandler(t.host, t.port, t.password, t.cipher, 30*time.Second) - } else { - udpHandler = dnsfallback.NewUDPHandler() - } - core.RegisterTCPConnHandler(oss.NewTCPHandler(t.host, t.port, t.password, t.cipher)) - core.RegisterUDPConnHandler(udpHandler) -} diff --git a/shadowsocks/connectivity.go b/shadowsocks/connectivity.go deleted file mode 100644 index 0a96e1ea..00000000 --- a/shadowsocks/connectivity.go +++ /dev/null @@ -1,106 +0,0 @@ -package shadowsocks - -import ( - "errors" - "net" - "net/http" - "time" - - shadowsocks "github.com/Jigsaw-Code/outline-ss-server/client" -) - -// TODO: make these values configurable by exposing a struct with the connectivity methods. -const ( - tcpTimeoutMs = udpTimeoutMs * udpMaxRetryAttempts - udpTimeoutMs = 1000 - udpMaxRetryAttempts = 5 - bufferLength = 512 -) - -// AuthenticationError is used to signal failed authentication to the Shadowsocks proxy. -type AuthenticationError struct { - error -} - -// ReachabilityError is used to signal an unreachable proxy. -type ReachabilityError struct { - error -} - -// CheckUDPConnectivityWithDNS determines whether the Shadowsocks proxy represented by `client` and -// the network support UDP traffic by issuing a DNS query though a resolver at `resolverAddr`. -// Returns nil on success or an error on failure. -func CheckUDPConnectivityWithDNS(client shadowsocks.Client, resolverAddr net.Addr) error { - conn, err := client.ListenUDP(nil) - if err != nil { - return err - } - defer conn.Close() - buf := make([]byte, bufferLength) - for attempt := 0; attempt < udpMaxRetryAttempts; attempt++ { - conn.SetDeadline(time.Now().Add(time.Millisecond * udpTimeoutMs)) - _, err := conn.WriteTo(getDNSRequest(), resolverAddr) - if err != nil { - continue - } - n, addr, err := conn.ReadFrom(buf) - if n == 0 && err != nil { - continue - } - if addr.String() != resolverAddr.String() { - continue // Ensure we got a response from the resolver. - } - return nil - } - return errors.New("UDP connectivity check timed out") -} - -// CheckTCPConnectivityWithHTTP determines whether the proxy is reachable over TCP and validates the -// client's authentication credentials by performing an HTTP HEAD request to `targetURL`, which must -// be of the form: http://[host](:[port])(/[path]). Returns nil on success, error if `targetURL` is -// invalid, AuthenticationError or ReachabilityError on connectivity failure. -func CheckTCPConnectivityWithHTTP(client shadowsocks.Client, targetURL string) error { - req, err := http.NewRequest("HEAD", targetURL, nil) - if err != nil { - return err - } - targetAddr := req.Host - if !hasPort(targetAddr) { - targetAddr = net.JoinHostPort(targetAddr, "80") - } - conn, err := client.DialTCP(nil, targetAddr) - if err != nil { - return &ReachabilityError{err} - } - defer conn.Close() - conn.SetDeadline(time.Now().Add(time.Millisecond * tcpTimeoutMs)) - err = req.Write(conn) - if err != nil { - return &AuthenticationError{err} - } - n, err := conn.Read(make([]byte, bufferLength)) - if n == 0 && err != nil { - return &AuthenticationError{err} - } - return nil -} - -func getDNSRequest() []byte { - return []byte{ - 0, 0, // [0-1] query ID - 1, 0, // [2-3] flags; byte[2] = 1 for recursion desired (RD). - 0, 1, // [4-5] QDCOUNT (number of queries) - 0, 0, // [6-7] ANCOUNT (number of answers) - 0, 0, // [8-9] NSCOUNT (number of name server records) - 0, 0, // [10-11] ARCOUNT (number of additional records) - 3, 'c', 'o', 'm', - 0, // null terminator of FQDN (root TLD) - 0, 1, // QTYPE, set to A - 0, 1, // QCLASS, set to 1 = IN (Internet) - } -} - -func hasPort(hostPort string) bool { - _, _, err := net.SplitHostPort(hostPort) - return err == nil -} diff --git a/shadowsocks/connectivity_test.go b/shadowsocks/connectivity_test.go deleted file mode 100644 index 30bdeae4..00000000 --- a/shadowsocks/connectivity_test.go +++ /dev/null @@ -1,133 +0,0 @@ -package shadowsocks - -import ( - "errors" - "net" - "reflect" - "testing" - "time" - - onet "github.com/Jigsaw-Code/outline-ss-server/net" - shadowsocks "github.com/Jigsaw-Code/outline-ss-server/client" -) - -func TestCheckUDPConnectivityWithDNS_Success(t *testing.T) { - client := &fakeSSClient{} - err := CheckUDPConnectivityWithDNS(client, shadowsocks.NewAddr("", "")) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } -} - -func TestCheckUDPConnectivityWithDNS_Fail(t *testing.T) { - client := &fakeSSClient{failUDP: true} - err := CheckUDPConnectivityWithDNS(client, shadowsocks.NewAddr("", "")) - if err == nil { - t.Fail() - } -} - -func TestCheckTCPConnectivityWithHTTP_Success(t *testing.T) { - client := &fakeSSClient{} - err := CheckTCPConnectivityWithHTTP(client, "") - if err != nil { - t.Fail() - } -} - -func TestCheckTCPConnectivityWithHTTP_FailReachability(t *testing.T) { - client := &fakeSSClient{failReachability: true} - err := CheckTCPConnectivityWithHTTP(client, "") - if err == nil { - t.Fail() - } - if _, ok := err.(*ReachabilityError); !ok { - t.Fatalf("Expected reachability error, got: %v", reflect.TypeOf(err)) - } -} - -func TestCheckTCPConnectivityWithHTTP_FailAuthentication(t *testing.T) { - client := &fakeSSClient{failAuthentication: true} - err := CheckTCPConnectivityWithHTTP(client, "") - if err == nil { - t.Fail() - } - if _, ok := err.(*AuthenticationError); !ok { - t.Fatalf("Expected authentication error, got: %v", reflect.TypeOf(err)) - } -} - -// Fake shadowsocks.Client that can be configured to return failing UDP and TCP connections. -type fakeSSClient struct { - failReachability bool - failAuthentication bool - failUDP bool -} - -func (c *fakeSSClient) DialTCP(laddr *net.TCPAddr, raddr string) (onet.DuplexConn, error) { - if c.failReachability { - return nil, &net.OpError{} - } - return &fakeDuplexConn{failRead: c.failAuthentication}, nil -} -func (c *fakeSSClient) ListenUDP(laddr *net.UDPAddr) (net.PacketConn, error) { - conn, err := net.ListenPacket("udp", "") - if err != nil { - return nil, err - } - // The UDP check should fail if any of the failure conditions are true since it is a superset of the others. - failRead := c.failAuthentication || c.failUDP || c.failReachability - return &fakePacketConn{PacketConn: conn, failRead: failRead}, nil -} - -// Fake PacketConn that fails `ReadFrom` calls when `failRead` is true. -type fakePacketConn struct { - net.PacketConn - addr net.Addr - failRead bool -} - -func (c *fakePacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { - c.addr = addr - return len(b), nil // Write always succeeds -} - -func (c *fakePacketConn) ReadFrom(b []byte) (int, net.Addr, error) { - if c.failRead { - return 0, c.addr, errors.New("Fake read error") - } - return len(b), c.addr, nil -} - -// Fake DuplexConn that fails `Read` calls when `failRead` is true. -type fakeDuplexConn struct { - onet.DuplexConn - failRead bool -} - -func (c *fakeDuplexConn) Read(b []byte) (int, error) { - if c.failRead { - return 0, errors.New("Fake read error") - } - return len(b), nil -} - -func (c *fakeDuplexConn) Write(b []byte) (int, error) { - return len(b), nil // Write always succeeds -} - -func (c *fakeDuplexConn) Close() error { return nil } - -func (c *fakeDuplexConn) LocalAddr() net.Addr { return nil } - -func (c *fakeDuplexConn) RemoteAddr() net.Addr { return nil } - -func (c *fakeDuplexConn) SetDeadline(t time.Time) error { return nil } - -func (c *fakeDuplexConn) SetReadDeadline(t time.Time) error { return nil } - -func (c *fakeDuplexConn) SetWriteDeadline(t time.Time) error { return nil } - -func (c *fakeDuplexConn) CloseRead() error { return nil } - -func (c *fakeDuplexConn) CloseWrite() error { return nil } diff --git a/shadowsocks/tcp.go b/shadowsocks/tcp.go deleted file mode 100644 index 6f2b2010..00000000 --- a/shadowsocks/tcp.go +++ /dev/null @@ -1,37 +0,0 @@ -package shadowsocks - -import ( - "net" - - onet "github.com/Jigsaw-Code/outline-ss-server/net" - shadowsocks "github.com/Jigsaw-Code/outline-ss-server/client" - "github.com/eycorsican/go-tun2socks/core" -) - -type tcpHandler struct { - client shadowsocks.Client -} - -// NewTCPHandler returns a Shadowsocks TCP connection handler. -// -// `host` is the hostname of the Shadowsocks proxy server. -// `port` is the port of the Shadowsocks proxy server. -// `password` is password used to authenticate to the server. -// `cipher` is the encryption cipher of the Shadowsocks proxy. -func NewTCPHandler(host string, port int, password, cipher string) core.TCPConnHandler { - client, err := shadowsocks.NewClient(host, port, password, cipher) - if err != nil { - return nil - } - return &tcpHandler{client} -} - -func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error { - proxyConn, err := h.client.DialTCP(nil, target.String()) - if err != nil { - return err - } - // TODO: Request upstream to make `conn` a `core.TCPConn` so we can avoid this type assertion. - go onet.Relay(conn.(core.TCPConn), proxyConn) - return nil -} diff --git a/shadowsocks/udp.go b/shadowsocks/udp.go deleted file mode 100644 index 3e0070af..00000000 --- a/shadowsocks/udp.go +++ /dev/null @@ -1,95 +0,0 @@ -package shadowsocks - -import ( - "fmt" - "net" - "sync" - "time" - - shadowsocks "github.com/Jigsaw-Code/outline-ss-server/client" - "github.com/eycorsican/go-tun2socks/core" -) - -type udpHandler struct { - sync.Mutex - - client shadowsocks.Client - timeout time.Duration - conns map[core.UDPConn]net.PacketConn -} - -// NewUDPHandler returns a Shadowsocks UDP connection handler. -// -// `host` is the hostname of the Shadowsocks proxy server. -// `port` is the port of the Shadowsocks proxy. -// `password` is password used to authenticate to the proxy. -// `cipher` is the encryption cipher of the Shadowsocks proxy. -// `timeout` is the UDP read and write timeout. -func NewUDPHandler(host string, port int, password, cipher string, timeout time.Duration) core.UDPConnHandler { - client, err := shadowsocks.NewClient(host, port, password, cipher) - if err != nil { - return nil - } - return &udpHandler{ - client: client, - timeout: timeout, - conns: make(map[core.UDPConn]net.PacketConn, 8), - } -} - -func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error { - proxyConn, err := h.client.ListenUDP(nil) - if err != nil { - return err - } - h.Lock() - h.conns[conn] = proxyConn - h.Unlock() - go h.handleDownstreamUDP(conn, proxyConn) - return nil -} - -func (h *udpHandler) handleDownstreamUDP(conn core.UDPConn, proxyConn net.PacketConn) { - buf := core.NewBytes(core.BufSize) - defer func() { - h.Close(conn) - core.FreeBytes(buf) - }() - for { - proxyConn.SetDeadline(time.Now().Add(h.timeout)) - n, addr, err := proxyConn.ReadFrom(buf) - if err != nil { - return - } - // No resolution will take place, the address sent by the proxy is a resolved IP. - udpAddr, err := net.ResolveUDPAddr("udp", addr.String()) - if err != nil { - return - } - _, err = conn.WriteFrom(buf[:n], udpAddr) - if err != nil { - return - } - } -} - -func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) error { - h.Lock() - proxyConn, ok := h.conns[conn] - h.Unlock() - if !ok { - return fmt.Errorf("connection %v->%v does not exist", conn.LocalAddr(), addr) - } - proxyConn.SetDeadline(time.Now().Add(h.timeout)) - _, err := proxyConn.WriteTo(data, addr) - return err -} - -func (h *udpHandler) Close(conn core.UDPConn) { - conn.Close() - h.Lock() - defer h.Unlock() - if proxyConn, ok := h.conns[conn]; ok { - proxyConn.Close() - } -} From ca841441d85b7757bc168f533681651ac8753649 Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Fri, 17 Sep 2021 18:51:20 +0530 Subject: [PATCH 09/17] go install instead of go get? --- make-aar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make-aar b/make-aar index f086a8ad..470473e7 100755 --- a/make-aar +++ b/make-aar @@ -30,7 +30,7 @@ export GO_COMPILED="$GOPATH/bin" export PATH="$GO_LANG:$GO_COMPILED:$PATH" # init gomobile -go get golang.org/x/mobile/cmd/gomobile +go install golang.org/x/mobile/cmd/gomobile gomobile init # checkout tagged branch? From e9e14606198d56d704f8f76879bf94c3971c87c3 Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Fri, 17 Sep 2021 19:03:25 +0530 Subject: [PATCH 10/17] rmv empty newline in go.mod --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index d22c9f23..23e793a2 100644 --- a/go.mod +++ b/go.mod @@ -19,5 +19,4 @@ require ( golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect - ) From 5075b7af772a9f39d9afc8cc3939bb0292e27d60 Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Sat, 18 Sep 2021 00:28:59 +0530 Subject: [PATCH 11/17] go get -d ./... breaks build, but it isn't required? --- make-aar | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/make-aar b/make-aar index 470473e7..c818f28f 100755 --- a/make-aar +++ b/make-aar @@ -36,8 +36,10 @@ gomobile init # checkout tagged branch? # git checkout -b "$VERSION" -# godeps -go get -d ./... +# godeps, for some reason fails with, +# "panic: Lookup called with empty package path" +# but works without it too, for now +# go get -d ./... # gomobile aar ./build_android.sh intra From dfc1acda0778cd98336e84adbf06387ded1acc46 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Mon, 31 Jan 2022 20:45:36 +0800 Subject: [PATCH 12/17] Fix build instructions in README.md According to the docs above. Android is the supported flavor. iOS, Linux, macOS & Windows are the unsupported, unmaintained builds. This might just be a typo to be fixed ;) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 101ebb06..74ac333c 100644 --- a/README.md +++ b/README.md @@ -76,5 +76,5 @@ go get -d ./... ./build_android.sh intra # other unsupported, unmaintained builds: -./build_[ios|android|macos|windows].sh +./build_[ios|linux|macos|windows].sh ``` From 4aaa204243b8cc1a6588aca8f97edf32de00b088 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Fri, 4 Feb 2022 19:37:15 +0800 Subject: [PATCH 13/17] Set DoH user agent to empty string for privacy --- intra/doh/doh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intra/doh/doh.go b/intra/doh/doh.go index 0c1f1fc8..be870bca 100644 --- a/intra/doh/doh.go +++ b/intra/doh/doh.go @@ -354,7 +354,7 @@ func (t *transport) sendRequest(id uint16, q []byte) (response []byte, hostname const mimetype = "application/dns-message" req.Header.Set("Content-Type", mimetype) req.Header.Set("Accept", mimetype) - req.Header.Set("User-Agent", "Intra") + req.Header.Set("User-Agent", "") log.Debugf("%d Sending query", id) httpResponse, err := t.client.Do(req) From 1ae58557ac94a4bbf315939ee7e80c6d96236eb1 Mon Sep 17 00:00:00 2001 From: Harish Date: Mon, 4 Apr 2022 20:53:11 +0530 Subject: [PATCH 14/17] mv nw engine to gvisor netstack, take 1 --- go.mod | 19 +- go.sum | 34 +-- intra/android/tun2socks.go | 28 +- intra/netstack/dispatchers.go | 224 ++++++++++++++++ intra/netstack/fdbased.go | 483 ++++++++++++++++++++++++++++++++++ intra/netstack/netstack.go | 74 ++++++ intra/netstack/tcp.go | 60 +++++ intra/tcp.go | 11 + intra/tunnel.go | 38 +++ tunnel/tun.go | 16 +- tunnel/tunnel.go | 36 ++- 11 files changed, 981 insertions(+), 42 deletions(-) create mode 100644 intra/netstack/dispatchers.go create mode 100644 intra/netstack/fdbased.go create mode 100644 intra/netstack/netstack.go create mode 100644 intra/netstack/tcp.go diff --git a/go.mod b/go.mod index 23e793a2..8ab90cad 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/celzero/firestack -go 1.15 +go 1.18 require ( github.com/Jigsaw-Code/getsni v1.0.0 @@ -14,9 +14,16 @@ require ( github.com/miekg/dns v1.1.43 github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/net v0.0.0-20210825183410-e898025ed96a - golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf - golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 // indirect - github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect - github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect + golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f + golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 + gvisor.dev/gvisor v0.0.0-20220329084425-9638e18d39b3 +) + +require ( + github.com/google/btree v1.0.1 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf // indirect + github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe // indirect + golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 // indirect + golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect ) diff --git a/go.sum b/go.sum index 40ffea67..15266b6e 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,6 @@ -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Jigsaw-Code/getsni v1.0.0 h1:OUTIu7wTBi/7DMX+RkZrN7XhU3UDevTEsAWK4gsqSwE= github.com/Jigsaw-Code/getsni v1.0.0/go.mod h1:Ps0Ec3fVMKLyAItVbMKoQFq1lDjtFQXZ+G5nRNNh/QE= -github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= -github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us= github.com/celzero/gotrie v0.0.0-20210413153406-d9d0dcea9cbd h1:hKeS1WzcKkGk/NfOIgTHHFhYgSvKpUEDTtg21VYbPVQ= github.com/celzero/gotrie v0.0.0-20210413153406-d9d0dcea9cbd/go.mod h1:Qo0txkBFM3m4+mXbyY6Pd46jCEUUHRd5C3Y4cSdA7jM= github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4 h1:lS3P5Nw3oPO05Lk2gFiYUOL3QPaH+fRoI1wFOc4G1UY= @@ -12,6 +9,8 @@ github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy0 github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/eycorsican/go-tun2socks v1.16.11 h1:+hJDNgisrYaGEqoSxhdikMgMJ4Ilfwm/IZDrWRrbaH8= github.com/eycorsican/go-tun2socks v1.16.11/go.mod h1:wgB2BFT8ZaPKyKOQ/5dljMG/YIow+AIXyq4KBwJ5sGQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868 h1:QZ79mRbNwYYYmiVjyv+X0NKgYE6nyN1yo3gtEFdzpiE= github.com/jedisct1/go-clocksmith v0.0.0-20210101121932-da382b963868/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw= github.com/jedisct1/go-dnsstamps v0.0.0-20210810213811-61cc83d2a354 h1:sIB9mDh2spQdh95jeXF2h9uSNtObbehD0YbDCzmqbM8= @@ -32,7 +31,7 @@ github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da h1:7x8pJcBTdKTBp github.com/txthinking/socks5 v0.0.0-20210716140126-fa1f52a8f2da/go.mod h1:7NloQcrxaZYKURWph5HLxVDlIwMHJXCPkeWPtpftsIg= github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe h1:gMWxZxBFRAXqoGkwkYlPX2zvyyKNWJpxOxCrjqJkm5A= github.com/txthinking/x v0.0.0-20210326105829-476fab902fbe/go.mod h1:WgqbSEmUYSjEV3B1qmee/PpP2NYEz4bL9/+mF1ma+s4= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -44,19 +43,19 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554 h1:3In5TnfvnuXTF/uflgpYxSCEGP2NdYT37KsPh3VjZYU= -golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E= +golang.org/x/mobile v0.0.0-20220325161704-447654d348e3 h1:ZDL7hDvJEQEcHVkoZawKmRUgbqn1pOIzb8EinBh5csU= +golang.org/x/mobile v0.0.0-20220325161704-447654d348e3/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -65,22 +64,25 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gvisor.dev/gvisor v0.0.0-20220329084425-9638e18d39b3 h1:jeCqiJy17qxcrzQIwflJycPPc5aPa86YbtStTk0hQYU= +gvisor.dev/gvisor v0.0.0-20220329084425-9638e18d39b3/go.mod h1:tWwEcFvJavs154OdjFCw78axNrsDlz4Zh8jvPqwcpGI= diff --git a/intra/android/tun2socks.go b/intra/android/tun2socks.go index f59c24ce..0a306897 100644 --- a/intra/android/tun2socks.go +++ b/intra/android/tun2socks.go @@ -21,9 +21,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -package tun2socks +package intra import ( + "errors" + "os" "runtime/debug" "strings" @@ -57,20 +59,28 @@ func init() { // // Throws an exception if the TUN file descriptor cannot be opened, or if the tunnel fails to // connect. -func ConnectIntraTunnel(fd int, fakedns string, dohdns doh.Transport, protector protect.Protector, flow protect.Flow, listener intra.Listener) (intra.Tunnel, error) { - tun, err := tunnel.MakeTunFile(fd) +const gvisor bool = true +const mtu uint32 = 1500 + +func ConnectIntraTunnel(fd int, fakedns string, dohdns doh.Transport, protector protect.Protector, flow protect.Flow, listener intra.Listener) (t intra.Tunnel, err error) { + dupfd, err := tunnel.Dup(fd) if err != nil { return nil, err } - dialer := protect.MakeDialer(protector) config := protect.MakeListenConfig(protector) - t, err := intra.NewTunnel(fakedns, dohdns, tun, dialer, flow, config, listener) - if err != nil { - return nil, err + if gvisor { + return intra.NewGTunnel(fakedns, dohdns, fd, mtu, dialer, flow, config, listener) + } else { + // java-land gives up its ownership of fd + tun := os.NewFile(uintptr(dupfd), "") + if tun == nil { + return nil, errors.New("failed to open TUN file descriptor") + } + t, err = intra.NewTunnel(fakedns, dohdns, tun, dialer, flow, config, listener) + go tunnel.ProcessInputPackets(t, tun) + return } - go tunnel.ProcessInputPackets(t, tun) - return t, nil } // NewDoHTransport returns a DNSTransport that connects to the specified DoH server. diff --git a/intra/netstack/dispatchers.go b/intra/netstack/dispatchers.go new file mode 100644 index 00000000..243f5493 --- /dev/null +++ b/intra/netstack/dispatchers.go @@ -0,0 +1,224 @@ +// Copyright (c) 2022 RethinkDNS and its authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// This file incorporates work covered by the following copyright and +// permission notice: +// +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Adopted from: github.com/google/gvisor/blob/f33d034/pkg/tcpip/link/fdbased/packet_dispatchers.go +package netstack + +import ( + "fmt" + + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/buffer" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" + "gvisor.dev/gvisor/pkg/tcpip/stack" +) + +// BufConfig defines the shape of the vectorised view used to read packets from the NIC. +var BufConfig = []int{128, 256, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768} + +type iovecBuffer struct { + // views are the actual buffers that hold the packet contents. + views []buffer.View + + // iovecs are initialized with base pointers/len of the corresponding + // entries in the views defined above, except when GSO is enabled + // (skipsVnetHdr) then the first iovec points to a buffer for the vnet header + // which is stripped before the views are passed up the stack for further + // processing. + iovecs []unix.Iovec + + // sizes is an array of buffer sizes for the underlying views. sizes is + // immutable. + sizes []int + + // skipsVnetHdr is true if virtioNetHdr is to skipped. + //skipsVnetHdr bool +} + +func newIovecBuffer(sizes []int) *iovecBuffer { + b := &iovecBuffer{ + views: make([]buffer.View, len(sizes)), + sizes: sizes, + // skipsVnetHdr: skipsVnetHdr, + } + /*niov := len(b.views) + if b.skipsVnetHdr { + niov++ + } + b.iovecs = make([]unix.Iovec, niov)*/ + b.iovecs = make([]unix.Iovec, len(b.views)) + return b +} + +func (b *iovecBuffer) nextIovecs() []unix.Iovec { + vnetHdrOff := 0 + /* if b.skipsVnetHdr { + var vnetHdr [virtioNetHdrSize]byte + // The kernel adds virtioNetHdr before each packet, but + // we don't use it, so so we allocate a buffer for it, + // add it in iovecs but don't add it in a view. + b.iovecs[0] = unix.Iovec{Base: &vnetHdr[0]} + b.iovecs[0].SetLen(virtioNetHdrSize) + vnetHdrOff++ + } + */for i := range b.views { + if b.views[i] != nil { + break + } + v := buffer.NewView(b.sizes[i]) + b.views[i] = v + b.iovecs[i+vnetHdrOff] = unix.Iovec{Base: &v[0]} + b.iovecs[i+vnetHdrOff].SetLen(len(v)) + } + return b.iovecs +} + +func (b *iovecBuffer) pullViews(n int) buffer.VectorisedView { + var views []buffer.View + c := 0 + /* if b.skipsVnetHdr { + c += virtioNetHdrSize + if c >= n { + // Nothing in the packet. + return buffer.NewVectorisedView(0, nil) + } + }*/ + for i, v := range b.views { + c += len(v) + if c >= n { + b.views[i].CapLength(len(v) - (c - n)) + views = append([]buffer.View(nil), b.views[:i+1]...) + break + } + } + // Remove the first len(views) used views from the state. + for i := range views { + b.views[i] = nil + } + /* if b.skipsVnetHdr { + // Exclude the size of the vnet header. + n -= virtioNetHdrSize + }*/ + return buffer.NewVectorisedView(n, views) +} + +// stopFd is an eventfd used to signal the stop of a dispatcher. +type stopFd struct { + efd int +} + +func newStopFd() (stopFd, error) { + efd, err := unix.Eventfd(0, unix.EFD_NONBLOCK) + if err != nil { + return stopFd{efd: -1}, fmt.Errorf("failed to create eventfd: %w", err) + } + return stopFd{efd: efd}, nil +} + +// stop writes to the eventfd and notifies the dispatcher to stop. It does not +// block. +func (s *stopFd) stop() { + increment := []byte{1, 0, 0, 0, 0, 0, 0, 0} + if n, err := unix.Write(s.efd, increment); n != len(increment) || err != nil { + // There are two possible errors documented in eventfd(2) for writing: + // 1. We are writing 8 bytes and not 0xffffffffffffff, thus no EINVAL. + // 2. stop is only supposed to be called once, it can't reach the limit, + // thus no EAGAIN. + panic(fmt.Sprintf("write(efd) = (%d, %s), want (%d, nil)", n, err, len(increment))) + } +} + +// readVDispatcher uses readv() system call to read inbound packets and +// dispatches them. +type readVDispatcher struct { + stopFd + // fd is the file descriptor used to send and receive packets. + fd int + + // e is the endpoint this dispatcher is attached to. + e *endpoint + + // buf is the iovec buffer that contains the packet contents. + buf *iovecBuffer +} + +func newReadVDispatcher(fd int, e *endpoint) (linkDispatcher, error) { + stopFd, err := newStopFd() + if err != nil { + return nil, err + } + d := &readVDispatcher{ + stopFd: stopFd, + fd: fd, + e: e, + } + + // skipsVnetHdr := d.e.gsoKind == stack.HWGSOSupported + d.buf = newIovecBuffer(BufConfig) + return d, nil +} + +// dispatch reads one packet from the file descriptor and dispatches it. +func (d *readVDispatcher) dispatch() (bool, tcpip.Error) { + n, err := rawfile.BlockingReadvUntilStopped(d.efd, d.fd, d.buf.nextIovecs()) + if n <= 0 || err != nil { + return false, err + } + + pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: d.buf.pullViews(n), + }) + defer pkt.DecRef() + + var p tcpip.NetworkProtocolNumber + // hdrSize always zero; unused + if d.e.hdrSize > 0 { + hdr, ok := pkt.LinkHeader().Consume(d.e.hdrSize) + if !ok { + return false, nil + } + p = header.Ethernet(hdr).Type() + } else { + // We don't get any indication of what the packet is, so try to guess + // if it's an IPv4 or IPv6 packet. + // IP version information is at the first octet, so pulling up 1 byte. + h, ok := pkt.Data().PullUp(1) + if !ok { + return true, nil + } + switch header.IPVersion(h) { + case header.IPv4Version: + p = header.IPv4ProtocolNumber + case header.IPv6Version: + p = header.IPv6ProtocolNumber + default: + return true, nil + } + } + + d.e.dispatcher.DeliverNetworkPacket(p, pkt) + + return true, nil +} diff --git a/intra/netstack/fdbased.go b/intra/netstack/fdbased.go new file mode 100644 index 00000000..70fcd4a1 --- /dev/null +++ b/intra/netstack/fdbased.go @@ -0,0 +1,483 @@ +// Copyright (c) 2022 RethinkDNS and its authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// This file incorporates work covered by the following copyright and +// permission notice: +// +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package netstack provides the implemention of data-link layer endpoints +// backed by boundary-preserving file descriptors (e.g., TUN devices, +// seqpacket/datagram sockets). +// +// Adopted from: github.com/google/gvisor/blob/f33d034/pkg/tcpip/link/fdbased/endpoint.go +package netstack + +import ( + "fmt" + "sync/atomic" + + "golang.org/x/sys/unix" + "gvisor.dev/gvisor/pkg/sync" + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/buffer" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/link/rawfile" + "gvisor.dev/gvisor/pkg/tcpip/stack" +) + +var _ stack.InjectableLinkEndpoint = (*endpoint)(nil) + +// linkDispatcher reads packets from the link FD and dispatches them to the +// NetworkDispatcher. +type linkDispatcher interface { + stop() + dispatch() (bool, tcpip.Error) +} + +type fdInfo struct { + fd int +} + +type endpoint struct { + // fds is the set of file descriptors each identifying one inbound/outbound + // channel. The endpoint will dispatch from all inbound channels as well as + // hash outbound packets to specific channels based on the packet hash. + fds []fdInfo + + // mtu (maximum transmission unit) is the maximum size of a packet. + mtu uint32 + + // hdrSize specifies the link-layer header size. If set to 0, no header + // is added/removed; otherwise an ethernet header is used. + hdrSize int + + // addr is the address of the endpoint. + addr tcpip.LinkAddress + + // caps holds the endpoint capabilities. + caps stack.LinkEndpointCapabilities + + inboundDispatchers []linkDispatcher + dispatcher stack.NetworkDispatcher + + // wg keeps track of running goroutines. + wg sync.WaitGroup + + // maxSyscallHeaderBytes has the same meaning as + // Options.MaxSyscallHeaderBytes. + maxSyscallHeaderBytes uintptr + + // writevMaxIovs is the maximum number of iovecs that may be passed to + // rawfile.NonBlockingWriteIovec, as possibly limited by + // maxSyscallHeaderBytes. (No analogous limit is defined for + // rawfile.NonBlockingSendMMsg, since in that case the maximum number of + // iovecs also depends on the number of mmsghdrs. Instead, if sendBatch + // encounters a packet whose iovec count is limited by + // maxSyscallHeaderBytes, it falls back to writing the packet using writev + // via WritePacket.) + writevMaxIovs int +} + +// Options specify the details about the fd-based endpoint to be created. +type Options struct { + // FDs is a set of FDs used to read/write packets. + FDs []int + + // MTU is the mtu to use for this endpoint. + MTU uint32 + + // EthernetHeader if true, indicates that the endpoint should read/write + // ethernet frames instead of IP packets. + EthernetHeader bool + + // Address is the link address for this endpoint. Only used if + // EthernetHeader is true. + Address tcpip.LinkAddress + + // SaveRestore if true, indicates that this NIC capability set should + // include CapabilitySaveRestore + SaveRestore bool + + // DisconnectOk if true, indicates that this NIC capability set should + // include CapabilityDisconnectOk. + DisconnectOk bool + + // TXChecksumOffload if true, indicates that this endpoints capability + // set should include CapabilityTXChecksumOffload. + TXChecksumOffload bool + + // RXChecksumOffload if true, indicates that this endpoints capability + // set should include CapabilityRXChecksumOffload. + RXChecksumOffload bool + + // If MaxSyscallHeaderBytes is non-zero, it is the maximum number of bytes + // of struct iovec, msghdr, and mmsghdr that may be passed by each host + // system call. + MaxSyscallHeaderBytes int +} + +// fanoutID is used for AF_PACKET based endpoints to enable PACKET_FANOUT +// support in the host kernel. This allows us to use multiple FD's to receive +// from the same underlying NIC. The fanoutID needs to be the same for a given +// set of FD's that point to the same NIC. Trying to set the PACKET_FANOUT +// option for an FD with a fanoutID already in use by another FD for a different +// NIC will return an EINVAL. +// +// Since fanoutID must be unique within the network namespace, we start with +// the PID to avoid collisions. The only way to be sure of avoiding collisions +// is to run in a new network namespace. +// +// Must be accessed using atomic operations. +var fanoutID int32 = int32(unix.Getpid()) + +// New creates a new fd-based endpoint. +// +// Makes fd non-blocking, but does not take ownership of fd, which must remain +// open for the lifetime of the returned endpoint (until after the endpoint has +// stopped being using and Wait returns). +func NewFdbasedInjectableEndpoint(opts *Options) (stack.LinkEndpoint, error) { + caps := stack.LinkEndpointCapabilities(0) + if opts.RXChecksumOffload { + caps |= stack.CapabilityRXChecksumOffload + } + + if opts.TXChecksumOffload { + caps |= stack.CapabilityTXChecksumOffload + } + + hdrSize := 0 + if opts.EthernetHeader { + hdrSize = header.EthernetMinimumSize + caps |= stack.CapabilityResolutionRequired + } + + if opts.SaveRestore { + caps |= stack.CapabilitySaveRestore + } + + if opts.DisconnectOk { + caps |= stack.CapabilityDisconnectOk + } + + if len(opts.FDs) == 0 { + return nil, fmt.Errorf("opts.FD is empty, at least one FD must be specified") + } + + if opts.MaxSyscallHeaderBytes < 0 { + return nil, fmt.Errorf("opts.MaxSyscallHeaderBytes is negative") + } + + e := &endpoint{ + mtu: opts.MTU, + caps: caps, + addr: opts.Address, + hdrSize: hdrSize, + // MaxSyscallHeaderBytes remains unused + maxSyscallHeaderBytes: uintptr(opts.MaxSyscallHeaderBytes), + writevMaxIovs: rawfile.MaxIovs, + } + if e.maxSyscallHeaderBytes != 0 { + if max := int(e.maxSyscallHeaderBytes / rawfile.SizeofIovec); max < e.writevMaxIovs { + e.writevMaxIovs = max + } + } + + // Increment fanoutID to ensure that we don't re-use the same fanoutID for + // the next endpoint. + fid := atomic.AddInt32(&fanoutID, 1) + + // Create per channel dispatchers. + for _, fd := range opts.FDs { + if err := unix.SetNonblock(fd, true); err != nil { + return nil, fmt.Errorf("unix.SetNonblock(%v) failed: %v", fd, err) + } + + e.fds = append(e.fds, fdInfo{fd: fd}) + + inboundDispatcher, err := createInboundDispatcher(e, fd, fid) + if err != nil { + return nil, fmt.Errorf("createInboundDispatcher(...) = %v", err) + } + e.inboundDispatchers = append(e.inboundDispatchers, inboundDispatcher) + } + + return e, nil +} + +func createInboundDispatcher(e *endpoint, fd int, fID int32) (linkDispatcher, error) { + // By default use the readv() dispatcher as it works with all kinds of + // FDs (tap/tun/unix domain sockets and af_packet). + inboundDispatcher, err := newReadVDispatcher(fd, e) + if err != nil { + return nil, fmt.Errorf("newReadVDispatcher(%d, %+v) = %v", fd, e, err) + } + return inboundDispatcher, nil +} + +// Attach launches the goroutine that reads packets from the file descriptor and +// dispatches them via the provided dispatcher. +func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) { + // nil means the NIC is being removed. + if dispatcher == nil && e.dispatcher != nil { + for _, dispatcher := range e.inboundDispatchers { + dispatcher.stop() + } + e.Wait() + e.dispatcher = nil + return + } + if dispatcher != nil && e.dispatcher == nil { + e.dispatcher = dispatcher + // Link endpoints are not savable. When transportation endpoints are + // saved, they stop sending outgoing packets and all incoming packets + // are rejected. + for i := range e.inboundDispatchers { + e.wg.Add(1) + go func(i int) { // S/R-SAFE: See above. + e.dispatchLoop(e.inboundDispatchers[i]) + e.wg.Done() + }(i) + } + } +} + +// IsAttached implements stack.LinkEndpoint.IsAttached. +func (e *endpoint) IsAttached() bool { + return e.dispatcher != nil +} + +// MTU implements stack.LinkEndpoint.MTU. It returns the value initialized +// during construction. +func (e *endpoint) MTU() uint32 { + return e.mtu +} + +// Capabilities implements stack.LinkEndpoint.Capabilities. +func (e *endpoint) Capabilities() stack.LinkEndpointCapabilities { + return e.caps +} + +// MaxHeaderLength returns the maximum size of the link-layer header. +func (e *endpoint) MaxHeaderLength() uint16 { + return uint16(e.hdrSize) +} + +// LinkAddress returns the link address of this endpoint. +func (e *endpoint) LinkAddress() tcpip.LinkAddress { + return e.addr +} + +// Wait implements stack.LinkEndpoint.Wait. It waits for the endpoint to stop +// reading from its FD. +func (e *endpoint) Wait() { + e.wg.Wait() +} + +// AddHeader implements stack.LinkEndpoint.AddHeader. +func (e *endpoint) AddHeader(pkt *stack.PacketBuffer) { + if e.hdrSize > 0 { + // Add ethernet header if needed. + eth := header.Ethernet(pkt.LinkHeader().Push(header.EthernetMinimumSize)) + eth.Encode(&header.EthernetFields{ + SrcAddr: pkt.EgressRoute.LocalLinkAddress, + DstAddr: pkt.EgressRoute.RemoteLinkAddress, + Type: pkt.NetworkProtocolNumber, + }) + } +} + +// writePacket writes outbound packets to the file descriptor. If it is not +// currently writable, the packet is dropped. +func (e *endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error { + fdInfo := e.fds[pkt.Hash%uint32(len(e.fds))] + fd := fdInfo.fd + // var vnetHdrBuf []byte + + views := pkt.Views() + numIovecs := len(views) + /*if len(vnetHdrBuf) != 0 { + numIovecs++ + }*/ + if numIovecs > e.writevMaxIovs { + numIovecs = e.writevMaxIovs + } + + // Allocate small iovec arrays on the stack. + var iovecsArr [8]unix.Iovec + iovecs := iovecsArr[:0] + if numIovecs > len(iovecsArr) { + iovecs = make([]unix.Iovec, 0, numIovecs) + } + //iovecs = rawfile.AppendIovecFromBytes(iovecs, vnetHdrBuf, numIovecs) + for _, v := range views { + iovecs = rawfile.AppendIovecFromBytes(iovecs, v, numIovecs) + } + return rawfile.NonBlockingWriteIovec(fd, iovecs) +} + +func (e *endpoint) sendBatch(batchFDInfo fdInfo, pkts []*stack.PacketBuffer) (int, tcpip.Error) { + // Send a batch of packets through batchFD. + batchFD := batchFDInfo.fd + mmsgHdrsStorage := make([]rawfile.MMsgHdr, 0, len(pkts)) + packets := 0 + for packets < len(pkts) { + mmsgHdrs := mmsgHdrsStorage + batch := pkts[packets:] + syscallHeaderBytes := uintptr(0) + for _, pkt := range batch { + // var vnetHdrBuf []byte + + views := pkt.Views() + numIovecs := len(views) + /*if len(vnetHdrBuf) != 0 { + numIovecs++ + }*/ + if numIovecs > rawfile.MaxIovs { + numIovecs = rawfile.MaxIovs + } + if e.maxSyscallHeaderBytes != 0 { + syscallHeaderBytes += rawfile.SizeofMMsgHdr + uintptr(numIovecs)*rawfile.SizeofIovec + if syscallHeaderBytes > e.maxSyscallHeaderBytes { + // We can't fit this packet into this call to sendmmsg(). + // We could potentially do so if we reduced numIovecs + // further, but this might incur considerable extra + // copying. Leave it to the next batch instead. + break + } + } + + // We can't easily allocate iovec arrays on the stack here since + // they will escape this loop iteration via mmsgHdrs. + iovecs := make([]unix.Iovec, 0, numIovecs) + // iovecs = rawfile.AppendIovecFromBytes(iovecs, vnetHdrBuf, numIovecs) + for _, v := range views { + iovecs = rawfile.AppendIovecFromBytes(iovecs, v, numIovecs) + } + + var mmsgHdr rawfile.MMsgHdr + mmsgHdr.Msg.Iov = &iovecs[0] + mmsgHdr.Msg.SetIovlen(len(iovecs)) + mmsgHdrs = append(mmsgHdrs, mmsgHdr) + } + + if len(mmsgHdrs) == 0 { + // We can't fit batch[0] into a mmsghdr while staying under + // e.maxSyscallHeaderBytes. Use WritePacket, which will avoid the + // mmsghdr (by using writev) and re-buffer iovecs more aggressively + // if necessary (by using e.writevMaxIovs instead of + // rawfile.MaxIovs). + pkt := batch[0] + if err := e.writePacket(pkt); err != nil { + return packets, err + } + packets++ + } else { + for len(mmsgHdrs) > 0 { + sent, err := rawfile.NonBlockingSendMMsg(batchFD, mmsgHdrs) + if err != nil { + return packets, err + } + packets += sent + mmsgHdrs = mmsgHdrs[sent:] + } + } + } + + return packets, nil +} + +// WritePackets writes outbound packets to the underlying file descriptors. If +// one is not currently writable, the packet is dropped. +// +// Being a batch API, each packet in pkts should have the following +// fields populated: +// - pkt.EgressRoute +// - pkt.GSOOptions +// - pkt.NetworkProtocolNumber +func (e *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error) { + // Preallocate to avoid repeated reallocation as we append to batch. + // batchSz is 47 because when SWGSO is in use then a single 65KB TCP + // segment can get split into 46 segments of 1420 bytes and a single 216 + // byte segment. + const batchSz = 47 + batch := make([]*stack.PacketBuffer, 0, batchSz) + batchFDInfo := fdInfo{fd: -1} + sentPackets := 0 + for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() { + if len(batch) == 0 { + batchFDInfo = e.fds[pkt.Hash%uint32(len(e.fds))] + } + pktFDInfo := e.fds[pkt.Hash%uint32(len(e.fds))] + if sendNow := pktFDInfo != batchFDInfo; !sendNow { + batch = append(batch, pkt) + continue + } + n, err := e.sendBatch(batchFDInfo, batch) + sentPackets += n + if err != nil { + return sentPackets, err + } + batch = batch[:0] + batch = append(batch, pkt) + batchFDInfo = pktFDInfo + } + + if len(batch) != 0 { + n, err := e.sendBatch(batchFDInfo, batch) + sentPackets += n + if err != nil { + return sentPackets, err + } + } + return sentPackets, nil +} + +// viewsEqual tests whether v1 and v2 refer to the same backing bytes. +func viewsEqual(vs1, vs2 []buffer.View) bool { + return len(vs1) == len(vs2) && (len(vs1) == 0 || &vs1[0] == &vs2[0]) +} + +// dispatchLoop reads packets from the file descriptor in a loop and dispatches +// them to the network stack. +func (e *endpoint) dispatchLoop(inboundDispatcher linkDispatcher) tcpip.Error { + for { + cont, err := inboundDispatcher.dispatch() + if err != nil || !cont { + return err + } + } +} + +// ARPHardwareType implements stack.LinkEndpoint.ARPHardwareType. +func (e *endpoint) ARPHardwareType() header.ARPHardwareType { + if e.hdrSize > 0 { + return header.ARPHardwareEther + } + return header.ARPHardwareNone +} + +// InjectInbound injects an inbound packet. +func (e *endpoint) InjectInbound(protocol tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) { + e.dispatcher.DeliverNetworkPacket(protocol, pkt) +} + +// InjectOutobund implements stack.InjectableEndpoint.InjectOutbound. +func (e *endpoint) InjectOutbound(dest tcpip.Address, packet []byte) tcpip.Error { + return rawfile.NonBlockingWrite(e.fds[0].fd, packet) +} diff --git a/intra/netstack/netstack.go b/intra/netstack/netstack.go new file mode 100644 index 00000000..a05d75a4 --- /dev/null +++ b/intra/netstack/netstack.go @@ -0,0 +1,74 @@ +// Copyright (c) 2022 RethinkDNS and its authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +package netstack + +import ( + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" + "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" +) + +func NewEndpoint(dev int, mtu uint32) (stack.LinkEndpoint, error) { + var endpoint stack.LinkEndpoint + var fd_array []int + fd_array[0] = int(dev) + opt := Options{ + FDs: fd_array, + MTU: mtu, + } + endpoint, _ = NewFdbasedInjectableEndpoint(&opt) + return endpoint, nil +} + +const nic tcpip.NICID = 0x01 + +func NewStack(handler GTCPConnHandler, endpoint stack.LinkEndpoint) (*stack.Stack, error) { + var o stack.Options + o = stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{ + ipv4.NewProtocol, + }, + TransportProtocols: []stack.TransportProtocolFactory{ + tcp.NewProtocol, + udp.NewProtocol, + icmp.NewProtocol4, + }, + } + + s := stack.New(o) + s.SetRouteTable([]tcpip.Route{ + { + Destination: header.IPv4EmptySubnet, + NIC: nic, + }, + { + Destination: header.IPv6EmptySubnet, + NIC: nic, + }, + }) + + // creates a fake nic and attaches netstack to it + assertNoErr(s.CreateNIC(nic, endpoint)) + // allow spoofing packets tuples + assertNoErr(s.SetSpoofing(nic, true)) + // allow all packets sent to our fake nic through to netstack + assertNoErr(s.SetPromiscuousMode(nic, true)) + setupTcpHandler(s, handler) + // setupUdpHandler(s, handler) + // setupIcmpHandler(s, endpoint, handler) + + return s, nil +} + +func assertNoErr(err tcpip.Error) { + if err != nil { + panic(err.String()) + } +} diff --git a/intra/netstack/tcp.go b/intra/netstack/tcp.go new file mode 100644 index 00000000..346dfadb --- /dev/null +++ b/intra/netstack/tcp.go @@ -0,0 +1,60 @@ +// Copyright (c) 2022 RethinkDNS and its authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +package netstack + +import ( + "fmt" + "net" + "time" + + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + "gvisor.dev/gvisor/pkg/waiter" +) + +type GTCPConnHandler interface { + NewConnection(conn GTCPConn, src, dst net.TCPAddr) +} + +func setupTcpHandler(s *stack.Stack, handler GTCPConnHandler) { + forwarder := tcp.NewForwarder(s, 0, 1024, func(request *tcp.ForwarderRequest) { + id := request.ID() + waitQueue := new(waiter.Queue) + endpoint, errT := request.CreateEndpoint(waitQueue) + if errT != nil { + fmt.Errorf("failed to create TCP connection") + // prevent potential half-open TCP connection leak. + request.Complete(true) + return + } + request.Complete(false) + src := net.TCPAddr{ + IP: net.IP(id.RemoteAddress), + Port: int(id.RemotePort), + } + + dst := net.TCPAddr{ + IP: net.IP(id.LocalAddress), + Port: int(id.LocalPort), + } + + go handler.NewConnection(GTCPConn{endpoint, gonet.NewTCPConn(waitQueue, endpoint)}, src, dst) + }) + s.SetTransportProtocolHandler(tcp.ProtocolNumber, forwarder.HandlePacket) +} + +type GTCPConn struct { + ep tcpip.Endpoint + *gonet.TCPConn +} + +func (g GTCPConn) Close() error { + g.ep.Close() + g.TCPConn.SetDeadline(time.Now().Add(-1)) + return g.TCPConn.Close() +} diff --git a/intra/tcp.go b/intra/tcp.go index c9d1414e..e8e13329 100644 --- a/intra/tcp.go +++ b/intra/tcp.go @@ -42,6 +42,7 @@ import ( "github.com/celzero/firestack/intra/dnscrypt" "github.com/celzero/firestack/intra/dnsproxy" "github.com/celzero/firestack/intra/doh" + "github.com/celzero/firestack/intra/netstack" "github.com/celzero/firestack/intra/protect" "github.com/celzero/firestack/intra/settings" "github.com/celzero/firestack/intra/split" @@ -50,6 +51,8 @@ import ( // TCPHandler is a core TCP handler that also supports DOH and splitting control. type TCPHandler interface { core.TCPConnHandler + netstack.GTCPConnHandler + SetDNS(doh.Transport) SetAlwaysSplitHTTPS(bool) blockConn(localConn net.Conn, target *net.TCPAddr) bool @@ -235,6 +238,14 @@ func (h *tcpHandler) onConn(localConn net.Conn, target *net.TCPAddr) (netid stri return } +func (h *tcpHandler) NewConnection(conn netstack.GTCPConn, src, dst net.TCPAddr) { + /*gconn := GTCPConn{C: conn} + newConn:= gconn.(net.Conn)*/ + if err := h.Handle(conn, &dst); err != nil { + conn.Close() + } +} + // TODO: Request upstream to make `conn` a `core.TCPConn` so we can avoid a type assertion. func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error { netid := h.onConn(conn, target) diff --git a/intra/tunnel.go b/intra/tunnel.go index fe21b2be..00e80c0e 100644 --- a/intra/tunnel.go +++ b/intra/tunnel.go @@ -38,6 +38,7 @@ import ( "github.com/celzero/firestack/intra/protect" "github.com/celzero/firestack/intra/rdns" "github.com/celzero/firestack/intra/settings" + "github.com/celzero/firestack/tunnel" ) @@ -106,6 +107,7 @@ func NewTunnel(fakedns string, dohdns doh.Transport, tunWriter io.WriteCloser, d if tunWriter == nil { return nil, errors.New("invalid tunnel writer") } + core.RegisterOutputFn(tunWriter.Write) t := &intratunnel{ Tunnel: tunnel.NewTunnel(tunWriter, core.NewLWIPStack()), @@ -118,6 +120,31 @@ func NewTunnel(fakedns string, dohdns doh.Transport, tunWriter io.WriteCloser, d return t, nil } +func NewGTunnel(fakedns string, dohdns doh.Transport, fd int, mtu uint32, dialer *net.Dialer, flow protect.Flow, config *net.ListenConfig, listener Listener) (Tunnel, error) { + fakednsaddr := net.TCPAddr{IP: net.ParseIP(fakedns)} + + tunmode := settings.DefaultTunMode() + + tcph := NewTCPHandler(fakednsaddr, dialer, flow, tunmode, listener) + t, err := tunnel.NewGTunnel(fd, mtu, tcph) + + if err != nil { + return nil, err + } + + gt := &intratunnel{ + Tunnel: t, + tunmode: tunmode, + } + + if err := gt.registerGConnectionHandlers(fakedns, dialer, flow, config, listener); err != nil { + return nil, err + } + + gt.SetDNS(dohdns) + return gt, nil +} + // Registers Intra's custom UDP and TCP connection handlers to the tun2socks core. func (t *intratunnel) registerConnectionHandlers(fakedns string, dialer *net.Dialer, flow protect.Flow, config *net.ListenConfig, listener Listener) error { // RFC 4787 REQ-5 requires a timeout no shorter than 5 minutes. @@ -139,6 +166,17 @@ func (t *intratunnel) registerConnectionHandlers(fakedns string, dialer *net.Dia return nil } +// FIXME: Remove, as this fn is similar to registerConnectionHandlers +func (t *intratunnel) registerGConnectionHandlers(fakedns string, dialer *net.Dialer, flow protect.Flow, config *net.ListenConfig, listener Listener) error { + tunmode := settings.DefaultTunMode() + tcpfakedns, err := net.ResolveTCPAddr("tcp", fakedns) + if err != nil { + return err + } + t.tcp = NewTCPHandler(*tcpfakedns, dialer, flow, tunmode, listener) + return nil +} + func (t *intratunnel) SetDNS(dns doh.Transport) { rethinkdns := t.rethinkdns t.dns = dns diff --git a/tunnel/tun.go b/tunnel/tun.go index 7b8ffd6d..a66625ab 100644 --- a/tunnel/tun.go +++ b/tunnel/tun.go @@ -24,9 +24,10 @@ package tunnel import ( "errors" - "golang.org/x/sys/unix" "os" + "golang.org/x/sys/unix" + "github.com/eycorsican/go-tun2socks/common/log" _ "github.com/eycorsican/go-tun2socks/common/log/simple" // Import simple log for the side effect of making logs printable. ) @@ -37,23 +38,18 @@ const vpnMtu = 1500 // The returned os.File holds a separate reference to the underlying file, // so the file will not be closed until both `fd` and the os.File are // separately closed. (UNIX only.) -func MakeTunFile(fd int) (*os.File, error) { +func Dup(fd int) (int, error) { if fd < 0 { - return nil, errors.New("Must provide a valid TUN file descriptor") + return -1, errors.New("Must provide a valid TUN file descriptor") } // Make a copy of `fd` so that os.File's finalizer doesn't close `fd`. newfd, err := unix.Dup(fd) if err != nil { - return nil, err + return -1, err } - // java-land gives up its ownership of fd - file := os.NewFile(uintptr(newfd), "") - if file == nil { - return nil, errors.New("Failed to open TUN file descriptor") - } - return file, nil + return newfd, nil } // ProcessInputPackets reads packets from a TUN device `tun` and writes them to `tunnel`. diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 4b6f2b6a..8d4d90f7 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -27,7 +27,9 @@ import ( "errors" "io" + "github.com/celzero/firestack/intra/netstack" "github.com/eycorsican/go-tun2socks/core" + "gvisor.dev/gvisor/pkg/tcpip/stack" ) // Tunnel represents a session on a TUN device. @@ -46,10 +48,21 @@ type tunnel struct { isConnected bool } +type gtunnel struct { + endpoint stack.LinkEndpoint + stack *stack.Stack + isConnected bool +} + func (t *tunnel) IsConnected() bool { return t.isConnected } +func (t *gtunnel) Disconnect() { + // FIXME: figure out what must be done here? + t.isConnected = false +} + func (t *tunnel) Disconnect() { if !t.isConnected { return @@ -59,13 +72,34 @@ func (t *tunnel) Disconnect() { t.tunWriter.Close() } +func (t *gtunnel) IsConnected() bool { + return t.isConnected +} + +func (t *gtunnel) Write([]byte) (int, error) { + return 0, errors.New("no write() on gvisor netstack") +} + func (t *tunnel) Write(data []byte) (int, error) { if !t.isConnected { - return 0, errors.New("Failed to write, network stack closed") + return 0, errors.New("failed to write, network stack closed") } return t.lwipStack.Write(data) } +func NewGTunnel(fd int, mtu uint32, tcphdl netstack.GTCPConnHandler) (Tunnel, error) { + endpoint, err := netstack.NewEndpoint(fd, mtu) + if err != nil { + return nil, err + } + stack, err := netstack.NewStack(tcphdl, endpoint) + if err != nil { + return nil, err + } + + return >unnel{endpoint, stack, true}, nil +} + func NewTunnel(tunWriter io.WriteCloser, lwipStack core.LWIPStack) Tunnel { return &tunnel{tunWriter, lwipStack, true} } From 37ead31d8d19663184edccc5e264456933e64b27 Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Mon, 2 May 2022 18:09:35 +0530 Subject: [PATCH 15/17] go in a bind due to conflicting package-name 'intra' --- intra/android/tun2socks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intra/android/tun2socks.go b/intra/android/tun2socks.go index 0a306897..d4abeb38 100644 --- a/intra/android/tun2socks.go +++ b/intra/android/tun2socks.go @@ -21,7 +21,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package intra +package tun2socks import ( "errors" From 41ec060b80d637b6704293abcad426810aebbd6e Mon Sep 17 00:00:00 2001 From: Murtaza Aliakbar Date: Mon, 2 May 2022 18:10:05 +0530 Subject: [PATCH 16/17] gomobile: disable debug and trimpath --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b242e5bc..5466b90a 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ MACOS_ARTIFACT=$(MACOS_BUILDDIR)/Tun2socks.framework WINDOWS_BUILDDIR=$(BUILDDIR)/windows LINUX_BUILDDIR=$(BUILDDIR)/linux -ANDROID_BUILD_CMD="$(GOBIND) -a -ldflags $(ANDROID_LDFLAGS) -target=android -tags android -work -o $(ANDROID_ARTIFACT)" +ANDROID_BUILD_CMD="$(GOBIND) -v -a -trimpath -ldflags $(ANDROID_LDFLAGS) -target=android -tags='android,disable_debug' -work -o $(ANDROID_ARTIFACT)" ANDROID_INTRA_BUILD_CMD="$(ANDROID_BUILD_CMD) $(IMPORT_PATH)/intra $(IMPORT_PATH)/intra/android $(IMPORT_PATH)/intra/doh $(IMPORT_PATH)/intra/split $(IMPORT_PATH)/intra/protect $(IMPORT_PATH)/intra/settings $(IMPORT_PATH)/intra/dnscrypt $(IMPORT_PATH)/intra/dnsproxy $(IMPORT_PATH)/intra/rdns $(IMPORT_PATH)/intra/xdns" IOS_BUILD_CMD="$(GOBIND) -a -ldflags $(LDFLAGS) -bundleid org.outline.tun2socks -target=ios/arm64 -tags ios -o $(IOS_ARTIFACT) $(IMPORT_PATH)/outline/apple $(IMPORT_PATH)/outline/shadowsocks" MACOS_BUILD_CMD="./tools/$(GOBIND) -a -ldflags $(LDFLAGS) -bundleid org.outline.tun2socks -target=ios/amd64 -tags ios -o $(MACOS_ARTIFACT) $(IMPORT_PATH)/outline/apple $(IMPORT_PATH)/outline/shadowsocks" From a896c72d52a507ab8f55dada9d5de705f1bef160 Mon Sep 17 00:00:00 2001 From: Harish Date: Thu, 2 Jun 2022 17:07:23 +0530 Subject: [PATCH 17/17] Add UDP.go --- intra/netstack/netstack.go | 33 ++++++++++-- intra/netstack/tcp.go | 4 +- intra/netstack/udp.go | 108 +++++++++++++++++++++++++++++++++++++ intra/tcp.go | 2 +- intra/tunnel.go | 11 +++- intra/udp.go | 23 +++++++- tunnel/tunnel.go | 5 +- 7 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 intra/netstack/udp.go diff --git a/intra/netstack/netstack.go b/intra/netstack/netstack.go index a05d75a4..88ded178 100644 --- a/intra/netstack/netstack.go +++ b/intra/netstack/netstack.go @@ -15,6 +15,32 @@ import ( "gvisor.dev/gvisor/pkg/tcpip/transport/udp" ) +type GConnHandler interface { + TCP() GTCPConnHandler + UDP() GUDPConnHandler +} + +type gconnhandler struct { + GConnHandler + tcp GTCPConnHandler + udp GUDPConnHandler +} + +func NewGConnHandler(tcp GTCPConnHandler, udp GUDPConnHandler) GConnHandler { + return &gconnhandler{ + tcp: tcp, + udp: udp, + } +} + +func (g *gconnhandler) TCP() GTCPConnHandler { + return g.tcp +} + +func (g *gconnhandler) UDP() GUDPConnHandler { + return g.udp +} + func NewEndpoint(dev int, mtu uint32) (stack.LinkEndpoint, error) { var endpoint stack.LinkEndpoint var fd_array []int @@ -29,7 +55,7 @@ func NewEndpoint(dev int, mtu uint32) (stack.LinkEndpoint, error) { const nic tcpip.NICID = 0x01 -func NewStack(handler GTCPConnHandler, endpoint stack.LinkEndpoint) (*stack.Stack, error) { +func NewStack(handler GConnHandler, endpoint stack.LinkEndpoint) (*stack.Stack, error) { var o stack.Options o = stack.Options{ NetworkProtocols: []stack.NetworkProtocolFactory{ @@ -60,8 +86,9 @@ func NewStack(handler GTCPConnHandler, endpoint stack.LinkEndpoint) (*stack.Stac assertNoErr(s.SetSpoofing(nic, true)) // allow all packets sent to our fake nic through to netstack assertNoErr(s.SetPromiscuousMode(nic, true)) - setupTcpHandler(s, handler) - // setupUdpHandler(s, handler) + setupTcpHandler(s, handler.TCP()) + setupUdpHandler(s, handler.UDP()) + //setupUdpHandler(s, handler) // setupIcmpHandler(s, endpoint, handler) return s, nil diff --git a/intra/netstack/tcp.go b/intra/netstack/tcp.go index 346dfadb..4cad0593 100644 --- a/intra/netstack/tcp.go +++ b/intra/netstack/tcp.go @@ -18,7 +18,7 @@ import ( ) type GTCPConnHandler interface { - NewConnection(conn GTCPConn, src, dst net.TCPAddr) + NewTCPConnection(conn GTCPConn, src, dst net.TCPAddr) } func setupTcpHandler(s *stack.Stack, handler GTCPConnHandler) { @@ -43,7 +43,7 @@ func setupTcpHandler(s *stack.Stack, handler GTCPConnHandler) { Port: int(id.LocalPort), } - go handler.NewConnection(GTCPConn{endpoint, gonet.NewTCPConn(waitQueue, endpoint)}, src, dst) + go handler.NewTCPConnection(GTCPConn{endpoint, gonet.NewTCPConn(waitQueue, endpoint)}, src, dst) }) s.SetTransportProtocolHandler(tcp.ProtocolNumber, forwarder.HandlePacket) } diff --git a/intra/netstack/udp.go b/intra/netstack/udp.go new file mode 100644 index 00000000..cff5acbe --- /dev/null +++ b/intra/netstack/udp.go @@ -0,0 +1,108 @@ +package netstack + +import ( + "net" + "strconv" + "time" + + // "github.com/v2fly/v2ray-core/v5/common/buf" + // v2rayNet "github.com/v2fly/v2ray-core/v5/common/net" + "github.com/eycorsican/go-tun2socks/core" + "gvisor.dev/gvisor/pkg/tcpip" + + "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" + + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" + "gvisor.dev/gvisor/pkg/waiter" + // "libcore/tun" +) + +var _ core.UDPConn = (*GUDPConn)(nil) + +type GUDPConnHandler interface { + NewUDPConnection(conn *GUDPConn, src, dst *net.UDPAddr) bool + HandleData(conn *GUDPConn, data []byte, addr *net.UDPAddr) error +} +type GUDPConn struct { + core.UDPConn + ep tcpip.Endpoint + gudp *gonet.UDPConn +} + +func setupUdpHandler(s *stack.Stack, handler GUDPConnHandler) { + forwarder := udp.NewForwarder(s, func(request *udp.ForwarderRequest) { + id := request.ID() + waitQueue := new(waiter.Queue) + endpoint, errT := request.CreateEndpoint(waitQueue) + if errT != nil { + + return + } + + src := &net.UDPAddr{ + IP: net.IP(id.RemoteAddress), + Port: int(id.RemotePort), + } + + dst := &net.UDPAddr{ + IP: net.IP(id.LocalAddress), + Port: int(id.LocalPort), + } + + gc := &GUDPConn{ + ep: endpoint, + gudp: gonet.NewUDPConn(s, waitQueue, endpoint), + } + + go func() { + ok := handler.NewUDPConnection(gc, src, dst) + + if !ok { + gc.gudp.Close() + return + } + const maxUDPReqSize = 1600 // FIXME: MTU + const readDeadline = 30 * time.Second // FIXME: Udp.Timeout + q := make([]byte, maxUDPReqSize) + for { + gc.gudp.SetReadDeadline(time.Now().Add(readDeadline)) + if n, addr, err := gc.gudp.ReadFrom(q); err == nil { + ipstr, portstr, _ := net.SplitHostPort(addr.String()) + port, _ := strconv.Atoi(portstr) + udpaddr := &net.UDPAddr{ + IP: net.ParseIP(ipstr), + Port: port, + } + handler.HandleData(gc, q[:n], udpaddr) + } else { + break + } + } + }() + }) + s.SetTransportProtocolHandler(udp.ProtocolNumber, forwarder.HandlePacket) +} + +func (g *GUDPConn) LocalAddr() *net.UDPAddr { + return g.gudp.LocalAddr().(*net.UDPAddr) +} + +// ReceiveTo will be called when data arrives from TUN, and the received +// data should be sent to addr. +func (g *GUDPConn) ReceiveTo(_ []byte, _ *net.UDPAddr) error { + return nil + // no-op; forwarder.HandlePacket takes care of this +} + +// WriteFrom writes data to TUN, addr will be set as source address of +// UDP packets that output to TUN. +func (g *GUDPConn) WriteFrom(data []byte, addr *net.UDPAddr) (int, error) { + // nb: write-deadlines set by intra.udp + return g.gudp.WriteTo(data, addr) +} + +// Close closes the connection. +func (g *GUDPConn) Close() error { + return g.gudp.Close() +} diff --git a/intra/tcp.go b/intra/tcp.go index e8e13329..f44fe2a8 100644 --- a/intra/tcp.go +++ b/intra/tcp.go @@ -238,7 +238,7 @@ func (h *tcpHandler) onConn(localConn net.Conn, target *net.TCPAddr) (netid stri return } -func (h *tcpHandler) NewConnection(conn netstack.GTCPConn, src, dst net.TCPAddr) { +func (h *tcpHandler) NewTCPConnection(conn netstack.GTCPConn, src, dst net.TCPAddr) { /*gconn := GTCPConn{C: conn} newConn:= gconn.(net.Conn)*/ if err := h.Handle(conn, &dst); err != nil { diff --git a/intra/tunnel.go b/intra/tunnel.go index 00e80c0e..b08a9df7 100644 --- a/intra/tunnel.go +++ b/intra/tunnel.go @@ -125,8 +125,17 @@ func NewGTunnel(fakedns string, dohdns doh.Transport, fd int, mtu uint32, dialer tunmode := settings.DefaultTunMode() + // RFC 4787 REQ-5 requires a timeout no shorter than 5 minutes. + udptimeout, _ := time.ParseDuration("5m") + + udpfakedns, err := net.ResolveUDPAddr("udp", fakedns) + if err != nil { + return nil, err + } + tcph := NewTCPHandler(fakednsaddr, dialer, flow, tunmode, listener) - t, err := tunnel.NewGTunnel(fd, mtu, tcph) + udph := NewUDPHandler(*udpfakedns, udptimeout, flow, tunmode, config, listener) + t, err := tunnel.NewGTunnel(fd, mtu, tcph, udph) if err != nil { return nil, err diff --git a/intra/udp.go b/intra/udp.go index dcfe9625..a14c5a80 100644 --- a/intra/udp.go +++ b/intra/udp.go @@ -44,6 +44,8 @@ import ( "github.com/celzero/firestack/intra/doh" "github.com/celzero/firestack/intra/protect" "github.com/celzero/firestack/intra/settings" + + "github.com/celzero/firestack/intra/netstack" ) // UDPSocketSummary describes a non-DNS UDP association, reported when it is discarded. @@ -73,6 +75,8 @@ func makeTracker(conn interface{}) *tracker { // UDPHandler adds DOH support to the base UDPConnHandler interface. type UDPHandler interface { core.UDPConnHandler + netstack.GUDPConnHandler + SetDNS(dns doh.Transport) onConn(localudp core.UDPConn, target *net.UDPAddr) string SetDNSCryptProxy(*dnscrypt.Proxy) @@ -97,7 +101,7 @@ type udpHandler struct { proxies map[string]*proxy.Dialer } -// NewUDPHandler makes a UDP handler with Intra-style DNS redirection: +// NewUDPHandler makes a UDP handler with Intra-style DNS redirection:udpHandler // All packets are routed directly to their destination, except packets whose // destination is `fakedns`. Those packets are redirected to DOH. // `timeout` controls the effective NAT mapping lifetime. @@ -176,8 +180,10 @@ func (h *udpHandler) onConn(localudp core.UDPConn, target *net.UDPAddr) (netid s if h.tunMode.BlockMode == settings.BlockModeNone { return protect.NetIdActive } + // localudp is either core.UDPConn or gonet.UDPConn wrapped in GUDPConn + uconn := localudp.LocalAddr() // Next-up If: BlockModeFilter or BlockModeFilterProc - return h.onNewConn(localudp.LocalAddr(), target) + return h.onNewConn(uconn, target) } func (h *udpHandler) onNewConn(source *net.UDPAddr, target *net.UDPAddr) (netid string) { @@ -199,6 +205,15 @@ func (h *udpHandler) onNewConn(source *net.UDPAddr, target *net.UDPAddr) (netid return } +func (h *udpHandler) NewUDPConnection(conn *netstack.GUDPConn, _, dst *net.UDPAddr) bool { + /*gconn := GTCPConn{C: conn} + newConn:= gconn.(net.Conn)*/ + if err := h.Connect(conn, dst); err != nil { + return false + } + return true +} + // Connect connects the proxy server. Note that target can be nil. func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error { netid := h.onConn(conn, target) @@ -361,6 +376,10 @@ func (h *udpHandler) dnsOverride(nat *tracker, conn core.UDPConn, addr *net.UDPA return false } +func (h *udpHandler) HandleData(conn *netstack.GUDPConn, data []byte, addr *net.UDPAddr) error { + return h.ReceiveTo(conn, data, addr) +} + // ReceiveTo is called when data arrives from conn (tun). func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) (err error) { h.RLock() diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 8d4d90f7..b6a6ae49 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -87,12 +87,13 @@ func (t *tunnel) Write(data []byte) (int, error) { return t.lwipStack.Write(data) } -func NewGTunnel(fd int, mtu uint32, tcphdl netstack.GTCPConnHandler) (Tunnel, error) { +func NewGTunnel(fd int, mtu uint32, tcph netstack.GTCPConnHandler, udph netstack.GUDPConnHandler) (Tunnel, error) { endpoint, err := netstack.NewEndpoint(fd, mtu) if err != nil { return nil, err } - stack, err := netstack.NewStack(tcphdl, endpoint) + ghdl := netstack.NewGConnHandler(tcph, udph) + stack, err := netstack.NewStack(ghdl, endpoint) if err != nil { return nil, err }