diff --git a/cmd/gost/register.go b/cmd/gost/register.go index f66d971..8beeb69 100644 --- a/cmd/gost/register.go +++ b/cmd/gost/register.go @@ -60,6 +60,7 @@ import ( _ "github.com/go-gost/gost/pkg/listener/tcp" _ "github.com/go-gost/gost/pkg/listener/tls" _ "github.com/go-gost/gost/pkg/listener/tls/mux" + _ "github.com/go-gost/gost/pkg/listener/tun" _ "github.com/go-gost/gost/pkg/listener/udp" _ "github.com/go-gost/gost/pkg/listener/ws" _ "github.com/go-gost/gost/pkg/listener/ws/mux" diff --git a/go.mod b/go.mod index f74aa5d..615fee0 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/magiconair/properties v1.8.5 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect + github.com/milosgajdos/tenus v0.0.3 github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect github.com/nxadm/tail v1.4.8 // indirect @@ -35,6 +36,7 @@ require ( github.com/shadowsocks/go-shadowsocks2 v0.1.4 github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 github.com/sirupsen/logrus v1.8.1 + github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -58,3 +60,5 @@ require ( gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 ) + +require github.com/docker/libcontainer v2.2.1+incompatible // indirect diff --git a/go.sum b/go.sum index 55b8a62..a4d660d 100644 --- a/go.sum +++ b/go.sum @@ -88,6 +88,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV 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/docker/libcontainer v2.2.1+incompatible h1:++SbbkCw+X8vAd4j2gOCzZ2Nn7s2xFALTf7LZKmM1/0= +github.com/docker/libcontainer v2.2.1+incompatible/go.mod h1:osvj61pYsqhNCMLGX31xr7klUBhHb/ZBuXS0o1Fvwbw= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -280,6 +282,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/milosgajdos/tenus v0.0.3 h1:jmaJzwaY1DUyYVD0lM4U+uvP2kkEg1VahDqRFxIkVBE= +github.com/milosgajdos/tenus v0.0.3/go.mod h1:eIjx29vNeDOYWJuCnaHY2r4fq5egetV26ry3on7p8qY= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -362,6 +366,8 @@ github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYED github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +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/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -440,7 +446,6 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -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/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -524,6 +529,7 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -621,6 +627,7 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc 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-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/connector/http/metadata.go b/pkg/connector/http/metadata.go index 301a39d..d3b3aea 100644 --- a/pkg/connector/http/metadata.go +++ b/pkg/connector/http/metadata.go @@ -1,13 +1,12 @@ package http import ( - "fmt" "net/http" "net/url" "strings" "time" - md "github.com/go-gost/gost/pkg/metadata" + mdata "github.com/go-gost/gost/pkg/metadata" ) type metadata struct { @@ -16,7 +15,7 @@ type metadata struct { header http.Header } -func (c *httpConnector) parseMetadata(md md.Metadata) (err error) { +func (c *httpConnector) parseMetadata(md mdata.Metadata) (err error) { const ( connectTimeout = "timeout" user = "user" @@ -34,12 +33,12 @@ func (c *httpConnector) parseMetadata(md md.Metadata) (err error) { } } - if mm, _ := md.Get(header).(map[interface{}]interface{}); len(mm) > 0 { - h := http.Header{} + if mm := mdata.GetStringMapString(md, header); len(mm) > 0 { + hd := http.Header{} for k, v := range mm { - h.Add(fmt.Sprintf("%v", k), fmt.Sprintf("%v", v)) + hd.Add(k, v) } - c.md.header = h + c.md.header = hd } return diff --git a/pkg/handler/http/metadata.go b/pkg/handler/http/metadata.go index 4e2cdac..43c8c19 100644 --- a/pkg/handler/http/metadata.go +++ b/pkg/handler/http/metadata.go @@ -1,12 +1,11 @@ package http import ( - "fmt" "net/http" "strings" "github.com/go-gost/gost/pkg/auth" - md "github.com/go-gost/gost/pkg/metadata" + mdata "github.com/go-gost/gost/pkg/metadata" ) type metadata struct { @@ -18,7 +17,7 @@ type metadata struct { header http.Header } -func (h *httpHandler) parseMetadata(md md.Metadata) error { +func (h *httpHandler) parseMetadata(md mdata.Metadata) error { const ( header = "header" users = "users" @@ -29,25 +28,23 @@ func (h *httpHandler) parseMetadata(md md.Metadata) error { enableUDP = "udp" ) - if v, _ := md.Get(users).([]interface{}); len(v) > 0 { + if auths := md.GetStrings(users); len(auths) > 0 { authenticator := auth.NewLocalAuthenticator(nil) - for _, auth := range v { - if s, _ := auth.(string); s != "" { - ss := strings.SplitN(s, ":", 2) - if len(ss) == 1 { - authenticator.Add(ss[0], "") - } else { - authenticator.Add(ss[0], ss[1]) - } + for _, auth := range auths { + ss := strings.SplitN(auth, ":", 2) + if len(ss) == 1 { + authenticator.Add(ss[0], "") + } else { + authenticator.Add(ss[0], ss[1]) } } h.md.authenticator = authenticator } - if mm, _ := md.Get(header).(map[interface{}]interface{}); len(mm) > 0 { + if mm := mdata.GetStringMapString(md, header); len(mm) > 0 { hd := http.Header{} for k, v := range mm { - hd.Add(fmt.Sprintf("%v", k), fmt.Sprintf("%v", v)) + hd.Add(k, v) } h.md.header = hd } diff --git a/pkg/handler/http2/metadata.go b/pkg/handler/http2/metadata.go index b2de45d..2455394 100644 --- a/pkg/handler/http2/metadata.go +++ b/pkg/handler/http2/metadata.go @@ -29,16 +29,14 @@ func (h *http2Handler) parseMetadata(md md.Metadata) error { h.md.proxyAgent = md.GetString(proxyAgent) - if v, _ := md.Get(users).([]interface{}); len(v) > 0 { + if auths := md.GetStrings(users); len(auths) > 0 { authenticator := auth.NewLocalAuthenticator(nil) - for _, auth := range v { - if s, _ := auth.(string); s != "" { - ss := strings.SplitN(s, ":", 2) - if len(ss) == 1 { - authenticator.Add(ss[0], "") - } else { - authenticator.Add(ss[0], ss[1]) - } + for _, auth := range auths { + ss := strings.SplitN(auth, ":", 2) + if len(ss) == 1 { + authenticator.Add(ss[0], "") + } else { + authenticator.Add(ss[0], ss[1]) } } h.md.authenticator = authenticator diff --git a/pkg/handler/relay/metadata.go b/pkg/handler/relay/metadata.go index 3fa1b71..91fde54 100644 --- a/pkg/handler/relay/metadata.go +++ b/pkg/handler/relay/metadata.go @@ -27,20 +27,19 @@ func (h *relayHandler) parseMetadata(md md.Metadata) (err error) { noDelay = "nodelay" ) - if v, _ := md.Get(users).([]interface{}); len(v) > 0 { + if auths := md.GetStrings(users); len(auths) > 0 { authenticator := auth.NewLocalAuthenticator(nil) - for _, auth := range v { - if s, _ := auth.(string); s != "" { - ss := strings.SplitN(s, ":", 2) - if len(ss) == 1 { - authenticator.Add(ss[0], "") - } else { - authenticator.Add(ss[0], ss[1]) - } + for _, auth := range auths { + ss := strings.SplitN(auth, ":", 2) + if len(ss) == 1 { + authenticator.Add(ss[0], "") + } else { + authenticator.Add(ss[0], ss[1]) } } h.md.authenticator = authenticator } + h.md.readTimeout = md.GetDuration(readTimeout) h.md.retryCount = md.GetInt(retryCount) h.md.enableBind = md.GetBool(enableBind) diff --git a/pkg/listener/obfs/http/metadata.go b/pkg/listener/obfs/http/metadata.go index 2d959b5..488b770 100644 --- a/pkg/listener/obfs/http/metadata.go +++ b/pkg/listener/obfs/http/metadata.go @@ -1,27 +1,26 @@ package http import ( - "fmt" "net/http" - md "github.com/go-gost/gost/pkg/metadata" + mdata "github.com/go-gost/gost/pkg/metadata" ) type metadata struct { header http.Header } -func (l *obfsListener) parseMetadata(md md.Metadata) (err error) { +func (l *obfsListener) parseMetadata(md mdata.Metadata) (err error) { const ( header = "header" ) - if mm, _ := md.Get(header).(map[interface{}]interface{}); len(mm) > 0 { - h := http.Header{} + if mm := mdata.GetStringMapString(md, header); len(mm) > 0 { + hd := http.Header{} for k, v := range mm { - h.Add(fmt.Sprintf("%v", k), fmt.Sprintf("%v", v)) + hd.Add(k, v) } - l.md.header = h + l.md.header = hd } return } diff --git a/pkg/listener/tun/conn.go b/pkg/listener/tun/conn.go new file mode 100644 index 0000000..56f3283 --- /dev/null +++ b/pkg/listener/tun/conn.go @@ -0,0 +1,46 @@ +package tun + +import ( + "errors" + "net" + "time" + + "github.com/songgao/water" +) + +type tunConn struct { + ifce *water.Interface + addr net.Addr +} + +func (c *tunConn) Read(b []byte) (n int, err error) { + return c.ifce.Read(b) +} + +func (c *tunConn) Write(b []byte) (n int, err error) { + return c.ifce.Write(b) +} + +func (c *tunConn) Close() (err error) { + return c.ifce.Close() +} + +func (c *tunConn) LocalAddr() net.Addr { + return c.addr +} + +func (c *tunConn) RemoteAddr() net.Addr { + return &net.IPAddr{} +} + +func (c *tunConn) SetDeadline(t time.Time) error { + return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} +} + +func (c *tunConn) SetReadDeadline(t time.Time) error { + return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} +} + +func (c *tunConn) SetWriteDeadline(t time.Time) error { + return &net.OpError{Op: "set", Net: "tuntap", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} +} diff --git a/pkg/listener/tun/listener.go b/pkg/listener/tun/listener.go new file mode 100644 index 0000000..3112d3c --- /dev/null +++ b/pkg/listener/tun/listener.go @@ -0,0 +1,87 @@ +package tun + +import ( + "net" + + "github.com/go-gost/gost/pkg/listener" + "github.com/go-gost/gost/pkg/logger" + md "github.com/go-gost/gost/pkg/metadata" + "github.com/go-gost/gost/pkg/registry" +) + +// ipRoute is an IP routing entry +type ipRoute struct { + Dest net.IPNet + Gateway net.IP +} + +func init() { + registry.RegisterListener("tun", NewListener) +} + +type tunListener struct { + saddr string + addr net.Addr + cqueue chan net.Conn + closed chan struct{} + logger logger.Logger + md metadata +} + +func NewListener(opts ...listener.Option) listener.Listener { + options := &listener.Options{} + for _, opt := range opts { + opt(options) + } + return &tunListener{ + saddr: options.Addr, + logger: options.Logger, + } +} + +func (l *tunListener) Init(md md.Metadata) (err error) { + if err = l.parseMetadata(md); err != nil { + return + } + + conn, ifce, err := l.createTun() + if err != nil { + return + } + + addrs, _ := ifce.Addrs() + l.logger.Infof("name: %s, net: %s, mtu: %d, addrs: %s", + ifce.Name, conn.LocalAddr(), ifce.MTU, addrs) + + l.addr = conn.LocalAddr() + l.cqueue = make(chan net.Conn, 1) + l.closed = make(chan struct{}) + + l.cqueue <- conn + + return +} + +func (l *tunListener) Accept() (net.Conn, error) { + select { + case conn := <-l.cqueue: + return conn, nil + case <-l.closed: + } + + return nil, listener.ErrClosed +} + +func (l *tunListener) Addr() net.Addr { + return l.addr +} + +func (l *tunListener) Close() error { + select { + case <-l.closed: + return net.ErrClosed + default: + close(l.closed) + } + return nil +} diff --git a/pkg/listener/tun/metadata.go b/pkg/listener/tun/metadata.go new file mode 100644 index 0000000..1b269ca --- /dev/null +++ b/pkg/listener/tun/metadata.go @@ -0,0 +1,70 @@ +package tun + +import ( + "net" + "strings" + + md "github.com/go-gost/gost/pkg/metadata" +) + +const ( + DefaultMTU = 1350 +) + +type metadata struct { + name string + net string + // peer addr of point-to-point on MacOS + peer string + mtu int + routes []ipRoute + // default gateway + gateway string + tcp bool +} + +func (l *tunListener) parseMetadata(md md.Metadata) (err error) { + const ( + name = "name" + netKey = "net" + peer = "peer" + mtu = "mtu" + routes = "routes" + gateway = "gw" + tcp = "tcp" + ) + + l.md.name = md.GetString(name) + l.md.net = md.GetString(netKey) + l.md.peer = md.GetString(peer) + l.md.mtu = md.GetInt(mtu) + + if l.md.mtu <= 0 { + l.md.mtu = DefaultMTU + } + + l.md.gateway = md.GetString(gateway) + l.md.tcp = md.GetBool(tcp) + + gw := net.ParseIP(l.md.gateway) + + for _, s := range md.GetStrings(routes) { + ss := strings.SplitN(s, " ", 2) + if len(ss) == 2 { + var route ipRoute + _, ipNet, _ := net.ParseCIDR(strings.TrimSpace(ss[0])) + if ipNet == nil { + continue + } + route.Dest = *ipNet + route.Gateway = net.ParseIP(ss[1]) + if route.Gateway == nil { + route.Gateway = gw + } + + l.md.routes = append(l.md.routes, route) + } + } + + return +} diff --git a/pkg/listener/tun/tun_darwin.go b/pkg/listener/tun/tun_darwin.go new file mode 100644 index 0000000..30091d9 --- /dev/null +++ b/pkg/listener/tun/tun_darwin.go @@ -0,0 +1,65 @@ +package tun + +import ( + "fmt" + "net" + "os/exec" + "strings" + + "github.com/songgao/water" +) + +func (l *tunListener) createTun() (conn net.Conn, itf *net.Interface, err error) { + ip, _, err := net.ParseCIDR(l.md.net) + if err != nil { + return + } + + ifce, err := water.New(water.Config{ + DeviceType: water.TUN, + }) + if err != nil { + return + } + + peer := l.md.peer + if peer == "" { + peer = ip.String() + } + + cmd := fmt.Sprintf("ifconfig %s inet %s %s mtu %d up", + ifce.Name(), l.md.net, l.md.peer, l.md.mtu) + l.logger.Debug(cmd) + + args := strings.Split(cmd, " ") + if err = exec.Command(args[0], args[1:]...).Run(); err != nil { + return + } + + if err = l.addRoutes(ifce.Name(), l.md.routes...); err != nil { + return + } + + itf, err = net.InterfaceByName(ifce.Name()) + if err != nil { + return + } + + conn = &tunConn{ + ifce: ifce, + addr: &net.IPAddr{IP: ip}, + } + return +} + +func (l *tunListener) addRoutes(ifName string, routes ...ipRoute) error { + for _, route := range routes { + cmd := fmt.Sprintf("route add -net %s -interface %s", route.Dest.String(), ifName) + l.logger.Debug(cmd) + args := strings.Split(cmd, " ") + if err := exec.Command(args[0], args[1:]...).Run(); err != nil { + return err + } + } + return nil +} diff --git a/pkg/listener/tun/tun_linux.go b/pkg/listener/tun/tun_linux.go new file mode 100644 index 0000000..891b52f --- /dev/null +++ b/pkg/listener/tun/tun_linux.go @@ -0,0 +1,75 @@ +package tun + +import ( + "errors" + "net" + "syscall" + + "github.com/docker/libcontainer/netlink" + "github.com/milosgajdos/tenus" + "github.com/songgao/water" +) + +func (l *tunListener) createTun() (conn net.Conn, itf *net.Interface, err error) { + ip, ipNet, err := net.ParseCIDR(l.md.net) + if err != nil { + return + } + + ifce, err := water.New(water.Config{ + DeviceType: water.TUN, + PlatformSpecificParams: water.PlatformSpecificParams{ + Name: l.md.name, + }, + }) + if err != nil { + return + } + + link, err := tenus.NewLinkFrom(ifce.Name()) + if err != nil { + return + } + + l.logger.Debugf("ip link set dev %s mtu %d", ifce.Name(), l.md.mtu) + + if err = link.SetLinkMTU(l.md.mtu); err != nil { + return + } + + l.logger.Debugf("ip address add %s dev %s", l.md.net, ifce.Name()) + + if err = link.SetLinkIp(ip, ipNet); err != nil { + return + } + + l.logger.Debugf("ip link set dev %s up", ifce.Name()) + if err = link.SetLinkUp(); err != nil { + return + } + + if err = l.addRoutes(ifce.Name(), l.md.routes...); err != nil { + return + } + + itf, err = net.InterfaceByName(ifce.Name()) + if err != nil { + return + } + + conn = &tunConn{ + ifce: ifce, + addr: &net.IPAddr{IP: ip}, + } + return +} + +func (l *tunListener) addRoutes(ifName string, routes ...ipRoute) error { + for _, route := range routes { + l.logger.Debugf("ip route add %s dev %s", route.Dest.String(), ifName) + if err := netlink.AddRoute(route.Dest.String(), "", "", ifName); err != nil && !errors.Is(err, syscall.EEXIST) { + return err + } + } + return nil +} diff --git a/pkg/listener/tun/tun_unix.go b/pkg/listener/tun/tun_unix.go new file mode 100644 index 0000000..24a1a1b --- /dev/null +++ b/pkg/listener/tun/tun_unix.go @@ -0,0 +1,62 @@ +//go:build !linux && !windows && !darwin + +package tun + +import ( + "fmt" + "net" + "os/exec" + "strings" + + "github.com/songgao/water" +) + +func (l *tunListener) createTun() (conn net.Conn, itf *net.Interface, err error) { + ip, _, err := net.ParseCIDR(l.md.net) + if err != nil { + return + } + + ifce, err := water.New(water.Config{ + DeviceType: water.TUN, + }) + if err != nil { + return + } + + cmd := fmt.Sprintf("ifconfig %s inet %s mtu %d up", + ifce.Name(), l.md.net, l.md.mtu) + l.logger.Debug(cmd) + args := strings.Split(cmd, " ") + if er := exec.Command(args[0], args[1:]...).Run(); er != nil { + err = fmt.Errorf("%s: %v", cmd, er) + return + } + + if err = l.addRoutes(ifce.Name(), l.md.routes...); err != nil { + return + } + + itf, err = net.InterfaceByName(ifce.Name()) + if err != nil { + return + } + + conn = &tunConn{ + ifce: ifce, + addr: &net.IPAddr{IP: ip}, + } + return +} + +func (l *tunListener) addRoutes(ifName string, routes ...ipRoute) error { + for _, route := range routes { + cmd := fmt.Sprintf("route add -net %s -interface %s", route.Dest.String(), ifName) + l.logger.Debug(cmd) + args := strings.Split(cmd, " ") + if er := exec.Command(args[0], args[1:]...).Run(); er != nil { + return fmt.Errorf("%s: %v", cmd, er) + } + } + return nil +} diff --git a/pkg/listener/tun/tun_windows.go b/pkg/listener/tun/tun_windows.go new file mode 100644 index 0000000..2451847 --- /dev/null +++ b/pkg/listener/tun/tun_windows.go @@ -0,0 +1,85 @@ +package tun + +import ( + "fmt" + "net" + "os/exec" + "strings" + + "github.com/songgao/water" +) + +func (l *tunListener) createTun() (conn net.Conn, itf *net.Interface, err error) { + ip, ipNet, err := net.ParseCIDR(l.md.net) + if err != nil { + return + } + + ifce, err := water.New(water.Config{ + DeviceType: water.TUN, + PlatformSpecificParams: water.PlatformSpecificParams{ + ComponentID: "tap0901", + InterfaceName: l.md.name, + Network: l.md.net, + }, + }) + if err != nil { + return + } + + cmd := fmt.Sprintf("netsh interface ip set address name=%s "+ + "source=static addr=%s mask=%s gateway=none", + ifce.Name(), ip.String(), ipMask(ipNet.Mask)) + l.logger.Debug(cmd) + + args := strings.Split(cmd, " ") + if er := exec.Command(args[0], args[1:]...).Run(); er != nil { + err = fmt.Errorf("%s: %v", cmd, er) + return + } + + if err = l.addRoutes(ifce.Name(), l.md.gateway, l.md.routes...); err != nil { + return + } + + itf, err = net.InterfaceByName(ifce.Name()) + if err != nil { + return + } + + conn = &tunConn{ + ifce: ifce, + addr: &net.IPAddr{IP: ip}, + } + return +} + +func (l *tunListener) addRoutes(ifName string, gw string, routes ...ipRoute) error { + for _, route := range routes { + l.deleteRoute(ifName, route.Dest.String()) + + cmd := fmt.Sprintf("netsh interface ip add route prefix=%s interface=%s store=active", + route.Dest.String(), ifName) + if gw != "" { + cmd += " nexthop=" + gw + } + l.logger.Debug(cmd) + args := strings.Split(cmd, " ") + if er := exec.Command(args[0], args[1:]...).Run(); er != nil { + return fmt.Errorf("%s: %v", cmd, er) + } + } + return nil +} + +func (l *tunListener) deleteRoute(ifName string, route string) error { + cmd := fmt.Sprintf("netsh interface ip delete route prefix=%s interface=%s store=active", + route, ifName) + l.logger.Debug(cmd) + args := strings.Split(cmd, " ") + return exec.Command(args[0], args[1:]...).Run() +} + +func ipMask(mask net.IPMask) string { + return fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) +} diff --git a/pkg/listener/ws/metadata.go b/pkg/listener/ws/metadata.go index 54df815..51b87a2 100644 --- a/pkg/listener/ws/metadata.go +++ b/pkg/listener/ws/metadata.go @@ -2,12 +2,11 @@ package ws import ( "crypto/tls" - "fmt" "net/http" "time" tls_util "github.com/go-gost/gost/pkg/common/util/tls" - md "github.com/go-gost/gost/pkg/metadata" + mdata "github.com/go-gost/gost/pkg/metadata" ) const ( @@ -29,7 +28,7 @@ type metadata struct { header http.Header } -func (l *wsListener) parseMetadata(md md.Metadata) (err error) { +func (l *wsListener) parseMetadata(md mdata.Metadata) (err error) { const ( certFile = "certFile" keyFile = "keyFile" @@ -72,13 +71,12 @@ func (l *wsListener) parseMetadata(md md.Metadata) (err error) { l.md.writeBufferSize = md.GetInt(writeBufferSize) l.md.enableCompression = md.GetBool(enableCompression) - if mm, _ := md.Get(header).(map[interface{}]interface{}); len(mm) > 0 { - h := http.Header{} + if mm := mdata.GetStringMapString(md, header); len(mm) > 0 { + hd := http.Header{} for k, v := range mm { - h.Add(fmt.Sprintf("%v", k), fmt.Sprintf("%v", v)) + hd.Add(k, v) } - l.md.header = h + l.md.header = hd } - return } diff --git a/pkg/listener/ws/mux/metadata.go b/pkg/listener/ws/mux/metadata.go index 783de36..3da2b4a 100644 --- a/pkg/listener/ws/mux/metadata.go +++ b/pkg/listener/ws/mux/metadata.go @@ -2,12 +2,11 @@ package mux import ( "crypto/tls" - "fmt" "net/http" "time" tls_util "github.com/go-gost/gost/pkg/common/util/tls" - md "github.com/go-gost/gost/pkg/metadata" + mdata "github.com/go-gost/gost/pkg/metadata" ) const ( @@ -35,7 +34,7 @@ type metadata struct { muxMaxStreamBuffer int } -func (l *mwsListener) parseMetadata(md md.Metadata) (err error) { +func (l *mwsListener) parseMetadata(md mdata.Metadata) (err error) { const ( path = "path" backlog = "backlog" @@ -91,12 +90,12 @@ func (l *mwsListener) parseMetadata(md md.Metadata) (err error) { l.md.muxMaxReceiveBuffer = md.GetInt(muxMaxReceiveBuffer) l.md.muxMaxStreamBuffer = md.GetInt(muxMaxStreamBuffer) - if mm, _ := md.Get(header).(map[interface{}]interface{}); len(mm) > 0 { - h := http.Header{} + if mm := mdata.GetStringMapString(md, header); len(mm) > 0 { + hd := http.Header{} for k, v := range mm { - h.Add(fmt.Sprintf("%v", k), fmt.Sprintf("%v", v)) + hd.Add(k, v) } - l.md.header = h + l.md.header = hd } return } diff --git a/pkg/metadata/metadata.go b/pkg/metadata/metadata.go index f1899ac..cba9f33 100644 --- a/pkg/metadata/metadata.go +++ b/pkg/metadata/metadata.go @@ -1,6 +1,7 @@ package metadata import ( + "fmt" "strconv" "time" ) @@ -12,8 +13,9 @@ type Metadata interface { GetBool(key string) bool GetInt(key string) int GetFloat(key string) float64 - GetString(key string) string GetDuration(key string) time.Duration + GetString(key string) string + GetStrings(key string) []string } type MapMetadata map[string]interface{} @@ -76,13 +78,6 @@ func (m MapMetadata) GetFloat(key string) (v float64) { return } -func (m MapMetadata) GetString(key string) (v string) { - if m != nil { - v, _ = m[key].(string) - } - return -} - func (m MapMetadata) GetDuration(key string) (v time.Duration) { if m != nil { switch vv := m[key].(type) { @@ -94,3 +89,31 @@ func (m MapMetadata) GetDuration(key string) (v time.Duration) { } return } + +func (m MapMetadata) GetString(key string) (v string) { + if m != nil { + v, _ = m[key].(string) + } + return +} + +func (m MapMetadata) GetStrings(key string) (ss []string) { + if v, _ := m.Get(key).([]interface{}); len(v) > 0 { + for _, vv := range v { + if s, ok := vv.(string); ok { + ss = append(ss, s) + } + } + } + return +} + +func GetStringMapString(md Metadata, key string) (m map[string]string) { + if mm, _ := md.Get(key).(map[interface{}]interface{}); len(mm) > 0 { + m = make(map[string]string) + for k, v := range mm { + m[fmt.Sprintf("%v", k)] = fmt.Sprintf("%v", v) + } + } + return +}