diff --git a/.travis.yml b/.travis.yml index 9f6a5537a..e47626bc8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,10 @@ language: go go: - 1.6.3 - -before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/onsi/gomega - - go get github.com/onsi/ginkgo +git: + depth: 5 + script: - go test -tags json github.com/v2ray/v2ray-core/... diff --git a/app/dispatcher/dispatcher.go b/app/dispatcher/dispatcher.go index a8902a3db..7af4c2f4d 100644 --- a/app/dispatcher/dispatcher.go +++ b/app/dispatcher/dispatcher.go @@ -2,7 +2,7 @@ package dispatcher import ( "github.com/v2ray/v2ray-core/app" - v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/transport/ray" ) @@ -12,5 +12,5 @@ const ( // PacketDispatcher dispatch a packet and possibly further network payload to its destination. type PacketDispatcher interface { - DispatchToOutbound(destination v2net.Destination) ray.InboundRay + DispatchToOutbound(meta *proxy.InboundHandlerMeta, session *proxy.SessionInfo) ray.InboundRay } diff --git a/app/dispatcher/impl/default.go b/app/dispatcher/impl/default.go index 0643cdc2f..5d601b202 100644 --- a/app/dispatcher/impl/default.go +++ b/app/dispatcher/impl/default.go @@ -4,6 +4,7 @@ import ( "github.com/v2ray/v2ray-core/app" "github.com/v2ray/v2ray-core/app/proxyman" "github.com/v2ray/v2ray-core/app/router" + "github.com/v2ray/v2ray-core/common/alloc" "github.com/v2ray/v2ray-core/common/log" v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/proxy" @@ -42,9 +43,10 @@ func (this *DefaultDispatcher) Release() { } -func (this *DefaultDispatcher) DispatchToOutbound(destination v2net.Destination) ray.InboundRay { +func (this *DefaultDispatcher) DispatchToOutbound(meta *proxy.InboundHandlerMeta, session *proxy.SessionInfo) ray.InboundRay { direct := ray.NewRay() dispatcher := this.ohm.GetDefaultHandler() + destination := session.Destination if this.router != nil { if tag, err := this.router.TakeDetour(destination); err == nil { @@ -59,7 +61,11 @@ func (this *DefaultDispatcher) DispatchToOutbound(destination v2net.Destination) } } - go this.FilterPacketAndDispatch(destination, direct, dispatcher) + if meta.AllowPassiveConnection { + go dispatcher.Dispatch(destination, alloc.NewLocalBuffer(32).Clear(), direct) + } else { + go this.FilterPacketAndDispatch(destination, direct, dispatcher) + } return direct } diff --git a/app/dispatcher/testing/dispatcher.go b/app/dispatcher/testing/dispatcher.go index 2ae1f1a03..7ff42da7a 100644 --- a/app/dispatcher/testing/dispatcher.go +++ b/app/dispatcher/testing/dispatcher.go @@ -2,6 +2,7 @@ package testing import ( v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/transport/ray" ) @@ -29,10 +30,10 @@ func NewTestPacketDispatcher(handler func(destination v2net.Destination, traffic } } -func (this *TestPacketDispatcher) DispatchToOutbound(destination v2net.Destination) ray.InboundRay { +func (this *TestPacketDispatcher) DispatchToOutbound(meta *proxy.InboundHandlerMeta, session *proxy.SessionInfo) ray.InboundRay { traffic := ray.NewRay() - this.Destination <- destination - go this.Handler(destination, traffic) + this.Destination <- session.Destination + go this.Handler(session.Destination, traffic) return traffic } diff --git a/app/dns/config_json.go b/app/dns/config_json.go index bd8665989..d1f6528f8 100644 --- a/app/dns/config_json.go +++ b/app/dns/config_json.go @@ -27,7 +27,7 @@ func (this *Config) UnmarshalJSON(data []byte) error { if jsonConfig.Hosts != nil { this.Hosts = make(map[string]net.IP) for domain, ip := range jsonConfig.Hosts { - if ip.Address.IsDomain() { + if ip.Address.Family().IsDomain() { return errors.New(ip.Address.String() + " is not an IP.") } this.Hosts[domain] = ip.Address.IP() diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index 26f9b3d50..9fbe2c8b5 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -10,6 +10,7 @@ import ( "github.com/v2ray/v2ray-core/common/dice" "github.com/v2ray/v2ray-core/common/log" v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/transport/internet/udp" "github.com/miekg/dns" @@ -49,9 +50,11 @@ type UDPNameServer struct { func NewUDPNameServer(address v2net.Destination, dispatcher dispatcher.PacketDispatcher) *UDPNameServer { s := &UDPNameServer{ - address: address, - requests: make(map[uint16]*PendingRequest), - udpServer: udp.NewUDPServer(dispatcher), + address: address, + requests: make(map[uint16]*PendingRequest), + udpServer: udp.NewUDPServer(&proxy.InboundHandlerMeta{ + AllowPassiveConnection: false, + }, dispatcher), } return s } @@ -162,7 +165,7 @@ func (this *UDPNameServer) BuildQueryA(domain string, id uint16) *alloc.Buffer { } func (this *UDPNameServer) DispatchQuery(payload *alloc.Buffer) { - this.udpServer.Dispatch(pseudoDestination, this.address, payload, this.HandleResponse) + this.udpServer.Dispatch(&proxy.SessionInfo{Source: pseudoDestination, Destination: this.address}, payload, this.HandleResponse) } func (this *UDPNameServer) QueryA(domain string) <-chan *ARecord { diff --git a/app/dns/server.go b/app/dns/server.go index f34fa83bb..1398d311c 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -42,7 +42,7 @@ func NewCacheServer(space app.Space, config *Config) *CacheServer { dispatcher := space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher) for idx, ns := range config.NameServers { - if ns.Address().IsDomain() && ns.Address().Domain() == "localhost" { + if ns.Address().Family().IsDomain() && ns.Address().Domain() == "localhost" { server.servers[idx] = &LocalNameServer{} } else { server.servers[idx] = NewUDPNameServer(ns, dispatcher) diff --git a/app/router/rules/condition.go b/app/router/rules/condition.go index 36fa1e213..40cec2fd1 100644 --- a/app/router/rules/condition.go +++ b/app/router/rules/condition.go @@ -73,7 +73,7 @@ func NewPlainDomainMatcher(pattern string) *PlainDomainMatcher { } func (this *PlainDomainMatcher) Apply(dest v2net.Destination) bool { - if !dest.Address().IsDomain() { + if !dest.Address().Family().IsDomain() { return false } domain := dest.Address().Domain() @@ -95,7 +95,7 @@ func NewRegexpDomainMatcher(pattern string) (*RegexpDomainMatcher, error) { } func (this *RegexpDomainMatcher) Apply(dest v2net.Destination) bool { - if !dest.Address().IsDomain() { + if !dest.Address().Family().IsDomain() { return false } domain := dest.Address().Domain() @@ -117,7 +117,7 @@ func NewCIDRMatcher(ipnet string) (*CIDRMatcher, error) { } func (this *CIDRMatcher) Apply(dest v2net.Destination) bool { - if !dest.Address().IsIPv4() && !dest.Address().IsIPv6() { + if !dest.Address().Family().Either(v2net.AddressFamilyIPv4, v2net.AddressFamilyIPv6) { return false } return this.cidr.Contains(dest.Address().IP()) @@ -134,7 +134,7 @@ func NewIPv4Matcher(ipnet *v2net.IPNet) *IPv4Matcher { } func (this *IPv4Matcher) Apply(dest v2net.Destination) bool { - if !dest.Address().IsIPv4() { + if !dest.Address().Family().Either(v2net.AddressFamilyIPv4) { return false } return this.ipv4net.Contains(dest.Address().IP()) diff --git a/app/router/rules/router.go b/app/router/rules/router.go index 306238970..786dd042a 100644 --- a/app/router/rules/router.go +++ b/app/router/rules/router.go @@ -64,7 +64,7 @@ func (this *Router) takeDetourWithoutCache(dest v2net.Destination) (string, erro return rule.Tag, nil } } - if this.config.DomainStrategy == UseIPIfNonMatch && dest.Address().IsDomain() { + if this.config.DomainStrategy == UseIPIfNonMatch && dest.Address().Family().IsDomain() { log.Info("Router: Looking up IP for ", dest) ipDests := this.ResolveIP(dest) if ipDests != nil { diff --git a/common/net/address.go b/common/net/address.go index 477a60757..490538f30 100644 --- a/common/net/address.go +++ b/common/net/address.go @@ -12,15 +12,41 @@ var ( AnyIP = IPAddress([]byte{0, 0, 0, 0}) ) +type AddressFamily int + +const ( + AddressFamilyIPv4 = AddressFamily(0) + AddressFamilyIPv6 = AddressFamily(1) + AddressFamilyDomain = AddressFamily(2) +) + +func (this AddressFamily) Either(fs ...AddressFamily) bool { + for _, f := range fs { + if this == f { + return true + } + } + return false +} + +func (this AddressFamily) IsIPv4() bool { + return this == AddressFamilyIPv4 +} + +func (this AddressFamily) IsIPv6() bool { + return this == AddressFamilyIPv6 +} + +func (this AddressFamily) IsDomain() bool { + return this == AddressFamilyDomain +} + // Address represents a network address to be communicated with. It may be an IP address or domain // address, not both. This interface doesn't resolve IP address for a given domain. type Address interface { IP() net.IP // IP of this Address Domain() string // Domain of this Address - - IsIPv4() bool // True if this Address is an IPv4 address - IsIPv6() bool // True if this Address is an IPv6 address - IsDomain() bool // True if this Address is an domain address + Family() AddressFamily String() string // String representation of this Address Equals(Address) bool @@ -75,16 +101,8 @@ func (addr *ipv4Address) Domain() string { panic("Calling Domain() on an IPv4Address.") } -func (addr *ipv4Address) IsIPv4() bool { - return true -} - -func (addr *ipv4Address) IsIPv6() bool { - return false -} - -func (addr *ipv4Address) IsDomain() bool { - return false +func (addr *ipv4Address) Family() AddressFamily { + return AddressFamilyIPv4 } func (this *ipv4Address) String() string { @@ -112,16 +130,8 @@ func (addr *ipv6Address) Domain() string { panic("Calling Domain() on an IPv6Address.") } -func (addr *ipv6Address) IsIPv4() bool { - return false -} - -func (addr *ipv6Address) IsIPv6() bool { - return true -} - -func (addr *ipv6Address) IsDomain() bool { - return false +func (this *ipv6Address) Family() AddressFamily { + return AddressFamilyIPv6 } func (this *ipv6Address) String() string { @@ -161,16 +171,8 @@ func (addr *domainAddress) Domain() string { return string(*addr) } -func (addr *domainAddress) IsIPv4() bool { - return false -} - -func (addr *domainAddress) IsIPv6() bool { - return false -} - -func (addr *domainAddress) IsDomain() bool { - return true +func (addr *domainAddress) Family() AddressFamily { + return AddressFamilyDomain } func (this *domainAddress) String() string { diff --git a/common/net/address_json_test.go b/common/net/address_json_test.go index 1344b3ef4..9ccd68ceb 100644 --- a/common/net/address_json_test.go +++ b/common/net/address_json_test.go @@ -18,8 +18,8 @@ func TestIPParsing(t *testing.T) { var address AddressJson err := json.Unmarshal([]byte(rawJson), &address) assert.Error(err).IsNil() - assert.Bool(address.Address.IsIPv4()).IsTrue() - assert.Bool(address.Address.IsDomain()).IsFalse() + assert.Bool(address.Address.Family().Either(AddressFamilyIPv4)).IsTrue() + assert.Bool(address.Address.Family().Either(AddressFamilyDomain)).IsFalse() assert.Bool(address.Address.IP().Equal(net.ParseIP("8.8.8.8"))).IsTrue() } @@ -30,8 +30,8 @@ func TestDomainParsing(t *testing.T) { var address AddressJson err := json.Unmarshal([]byte(rawJson), &address) assert.Error(err).IsNil() - assert.Bool(address.Address.IsIPv4()).IsFalse() - assert.Bool(address.Address.IsDomain()).IsTrue() + assert.Bool(address.Address.Family().Either(AddressFamilyIPv4)).IsFalse() + assert.Bool(address.Address.Family().Either(AddressFamilyDomain)).IsTrue() assert.String(address.Address.Domain()).Equals("v2ray.com") } diff --git a/common/net/destination.go b/common/net/destination.go index 0641bc7b1..57510e5d9 100644 --- a/common/net/destination.go +++ b/common/net/destination.go @@ -1,5 +1,9 @@ package net +import ( + "net" +) + // Destination represents a network destination including address and protocol (tcp / udp). type Destination interface { Network() Network // Protocol of communication (tcp / udp) @@ -13,6 +17,17 @@ type Destination interface { IsUDP() bool // True if destination is reachable via UDP } +func DestinationFromAddr(addr net.Addr) Destination { + switch addr := addr.(type) { + case *net.TCPAddr: + return TCPDestination(IPAddress(addr.IP), Port(addr.Port)) + case *net.UDPAddr: + return UDPDestination(IPAddress(addr.IP), Port(addr.Port)) + default: + panic("Unknown address type.") + } +} + // TCPDestination creates a TCP destination with given address func TCPDestination(address Address, port Port) Destination { return &tcpDestination{address: address, port: port} diff --git a/core.go b/core.go index ad0cdbdf0..8071835d2 100644 --- a/core.go +++ b/core.go @@ -8,7 +8,7 @@ import ( ) var ( - version = "1.24" + version = "1.25" build = "Custom" codename = "New Order" intro = "An unified platform for anti-censorship." diff --git a/proxy/blackhole/config_json_test.go b/proxy/blackhole/config_json_test.go index a61537eaa..b9094a940 100644 --- a/proxy/blackhole/config_json_test.go +++ b/proxy/blackhole/config_json_test.go @@ -1,3 +1,5 @@ +// +build json + package blackhole_test import ( diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 42d3b25aa..7d38d9f51 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -89,7 +89,7 @@ func (this *DokodemoDoor) Start() error { } func (this *DokodemoDoor) ListenUDP() error { - this.udpServer = udp.NewUDPServer(this.packetDispatcher) + this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher) udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handleUDPPackets) if err != nil { log.Error("Dokodemo failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err) @@ -102,7 +102,8 @@ func (this *DokodemoDoor) ListenUDP() error { } func (this *DokodemoDoor) handleUDPPackets(payload *alloc.Buffer, dest v2net.Destination) { - this.udpServer.Dispatch(dest, v2net.UDPDestination(this.address, this.port), payload, this.handleUDPResponse) + this.udpServer.Dispatch( + &proxy.SessionInfo{Source: dest, Destination: v2net.UDPDestination(this.address, this.port)}, payload, this.handleUDPResponse) } func (this *DokodemoDoor) handleUDPResponse(dest v2net.Destination, payload *alloc.Buffer) { @@ -148,7 +149,10 @@ func (this *DokodemoDoor) HandleTCPConnection(conn internet.Connection) { } log.Info("Dokodemo: Handling request to ", dest) - ray := this.packetDispatcher.DispatchToOutbound(dest) + ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{ + Source: v2net.DestinationFromAddr(conn.RemoteAddr()), + Destination: dest, + }) defer ray.InboundOutput().Release() var inputFinish, outputFinish sync.Mutex diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 9870b90d9..089639fea 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -47,7 +47,7 @@ func NewFreedomConnection(config *Config, space app.Space, meta *proxy.OutboundH // @Private func (this *FreedomConnection) ResolveIP(destination v2net.Destination) v2net.Destination { - if !destination.Address().IsDomain() { + if !destination.Address().Family().IsDomain() { return destination } @@ -76,7 +76,7 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload * defer ray.OutboundOutput().Close() var conn internet.Connection - if this.domainStrategy == DomainStrategyUseIP && destination.Address().IsDomain() { + if this.domainStrategy == DomainStrategyUseIP && destination.Address().Family().IsDomain() { destination = this.ResolveIP(destination) } err := retry.Timed(5, 100).On(func() error { diff --git a/proxy/http/server.go b/proxy/http/server.go index 453b04c9d..82ad004c3 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -119,14 +119,18 @@ func (this *Server) handleConnection(conn internet.Connection) { return } log.Access(conn.RemoteAddr(), request.URL, log.AccessAccepted, "") + session := &proxy.SessionInfo{ + Source: v2net.DestinationFromAddr(conn.RemoteAddr()), + Destination: dest, + } if strings.ToUpper(request.Method) == "CONNECT" { - this.handleConnect(request, dest, reader, conn) + this.handleConnect(request, session, reader, conn) } else { - this.handlePlainHTTP(request, dest, reader, conn) + this.handlePlainHTTP(request, session, reader, conn) } } -func (this *Server) handleConnect(request *http.Request, destination v2net.Destination, reader io.Reader, writer io.Writer) { +func (this *Server) handleConnect(request *http.Request, session *proxy.SessionInfo, reader io.Reader, writer io.Writer) { response := &http.Response{ Status: "200 OK", StatusCode: 200, @@ -140,7 +144,7 @@ func (this *Server) handleConnect(request *http.Request, destination v2net.Desti } response.Write(writer) - ray := this.packetDispatcher.DispatchToOutbound(destination) + ray := this.packetDispatcher.DispatchToOutbound(this.meta, session) this.transport(reader, writer, ray) } @@ -209,7 +213,7 @@ func (this *Server) GenerateResponse(statusCode int, status string) *http.Respon } } -func (this *Server) handlePlainHTTP(request *http.Request, dest v2net.Destination, reader *bufio.Reader, writer io.Writer) { +func (this *Server) handlePlainHTTP(request *http.Request, session *proxy.SessionInfo, reader *bufio.Reader, writer io.Writer) { if len(request.URL.Host) <= 0 { response := this.GenerateResponse(400, "Bad Request") response.Write(writer) @@ -220,7 +224,7 @@ func (this *Server) handlePlainHTTP(request *http.Request, dest v2net.Destinatio request.Host = request.URL.Host StripHopByHopHeaders(request) - ray := this.packetDispatcher.DispatchToOutbound(dest) + ray := this.packetDispatcher.DispatchToOutbound(this.meta, session) defer ray.InboundInput().Close() defer ray.InboundOutput().Release() diff --git a/proxy/proxy.go b/proxy/proxy.go index c8ee37e9d..d6cce0560 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -4,6 +4,7 @@ package proxy // import "github.com/v2ray/v2ray-core/proxy" import ( "github.com/v2ray/v2ray-core/common/alloc" v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/transport/internet" "github.com/v2ray/v2ray-core/transport/ray" ) @@ -15,11 +16,18 @@ const ( HandlerStateRunning = HandlerState(1) ) +type SessionInfo struct { + Source v2net.Destination + Destination v2net.Destination + User *protocol.User +} + type InboundHandlerMeta struct { - Tag string - Address v2net.Address - Port v2net.Port - StreamSettings *internet.StreamSettings + Tag string + Address v2net.Address + Port v2net.Port + AllowPassiveConnection bool + StreamSettings *internet.StreamSettings } type OutboundHandlerMeta struct { diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 884927c7c..12cace63b 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -70,7 +70,7 @@ func (this *Server) Start() error { this.tcpHub = tcpHub if this.config.UDP { - this.udpServer = udp.NewUDPServer(this.packetDispatcher) + this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher) udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handlerUDPPayload) if err != nil { log.Error("Shadowsocks: Failed to listen UDP on ", this.meta.Address, ":", this.meta.Port, ": ", err) @@ -114,7 +114,7 @@ func (this *Server) handlerUDPPayload(payload *alloc.Buffer, source v2net.Destin log.Access(source, dest, log.AccessAccepted, "") log.Info("Shadowsocks: Tunnelling request to ", dest) - this.udpServer.Dispatch(source, dest, request.DetachUDPPayload(), func(destination v2net.Destination, payload *alloc.Buffer) { + this.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: dest}, request.DetachUDPPayload(), func(destination v2net.Destination, payload *alloc.Buffer) { defer payload.Release() response := alloc.NewBuffer().Slice(0, ivLen) @@ -131,14 +131,14 @@ func (this *Server) handlerUDPPayload(payload *alloc.Buffer, source v2net.Destin writer := crypto.NewCryptionWriter(stream, response) - switch { - case request.Address.IsIPv4(): + switch request.Address.Family() { + case v2net.AddressFamilyIPv4: writer.Write([]byte{AddrTypeIPv4}) writer.Write(request.Address.IP()) - case request.Address.IsIPv6(): + case v2net.AddressFamilyIPv6: writer.Write([]byte{AddrTypeIPv6}) writer.Write(request.Address.IP()) - case request.Address.IsDomain(): + case v2net.AddressFamilyDomain: writer.Write([]byte{AddrTypeDomain, byte(len(request.Address.Domain()))}) writer.Write([]byte(request.Address.Domain())) } @@ -204,7 +204,10 @@ func (this *Server) handleConnection(conn internet.Connection) { log.Access(conn.RemoteAddr(), dest, log.AccessAccepted, "") log.Info("Shadowsocks: Tunnelling request to ", dest) - ray := this.packetDispatcher.DispatchToOutbound(dest) + ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{ + Source: v2net.DestinationFromAddr(conn.RemoteAddr()), + Destination: dest, + }) defer ray.InboundOutput().Release() var writeFinish sync.Mutex diff --git a/proxy/socks/protocol/udp.go b/proxy/socks/protocol/udp.go index b68dc473f..77912c928 100644 --- a/proxy/socks/protocol/udp.go +++ b/proxy/socks/protocol/udp.go @@ -26,12 +26,12 @@ func (request *Socks5UDPRequest) Destination() v2net.Destination { func (request *Socks5UDPRequest) Write(buffer *alloc.Buffer) { buffer.AppendBytes(0, 0, request.Fragment) - switch { - case request.Address.IsIPv4(): + switch request.Address.Family() { + case v2net.AddressFamilyIPv4: buffer.AppendBytes(AddrTypeIPv4).Append(request.Address.IP()) - case request.Address.IsIPv6(): + case v2net.AddressFamilyIPv6: buffer.AppendBytes(AddrTypeIPv6).Append(request.Address.IP()) - case request.Address.IsDomain(): + case v2net.AddressFamilyDomain: buffer.AppendBytes(AddrTypeDomain, byte(len(request.Address.Domain()))).Append([]byte(request.Address.Domain())) } buffer.AppendUint16(request.Port.Value()) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 90b0f5625..dae731589 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -119,7 +119,7 @@ func (this *Server) handleConnection(connection internet.Connection) { return } - clientAddr := connection.RemoteAddr().String() + clientAddr := v2net.DestinationFromAddr(connection.RemoteAddr()) if err != nil && err == protocol.Socks4Downgrade { this.handleSocks4(clientAddr, reader, writer, auth4) } else { @@ -127,7 +127,7 @@ func (this *Server) handleConnection(connection internet.Connection) { } } -func (this *Server) handleSocks5(clientAddr string, reader *v2io.BufferedReader, writer *v2io.BufferedWriter, auth protocol.Socks5AuthenticationRequest) error { +func (this *Server) handleSocks5(clientAddr v2net.Destination, reader *v2io.BufferedReader, writer *v2io.BufferedWriter, auth protocol.Socks5AuthenticationRequest) error { expectedAuthMethod := protocol.AuthNotRequired if this.config.AuthType == AuthTypePassword { expectedAuthMethod = protocol.AuthUserPass @@ -219,10 +219,14 @@ func (this *Server) handleSocks5(clientAddr string, reader *v2io.BufferedReader, writer.SetCached(false) dest := request.Destination() + session := &proxy.SessionInfo{ + Source: clientAddr, + Destination: dest, + } log.Info("Socks: TCP Connect request to ", dest) log.Access(clientAddr, dest, log.AccessAccepted, "") - this.transport(reader, writer, dest) + this.transport(reader, writer, session) return nil } @@ -233,12 +237,12 @@ func (this *Server) handleUDP(reader io.Reader, writer *v2io.BufferedWriter) err udpAddr := this.udpAddress response.Port = udpAddr.Port() - switch { - case udpAddr.Address().IsIPv4(): + switch udpAddr.Address().Family() { + case v2net.AddressFamilyIPv4: response.SetIPv4(udpAddr.Address().IP()) - case udpAddr.Address().IsIPv6(): + case v2net.AddressFamilyIPv6: response.SetIPv6(udpAddr.Address().IP()) - case udpAddr.Address().IsDomain(): + case v2net.AddressFamilyDomain: response.SetDomain(udpAddr.Address().Domain()) } @@ -258,7 +262,7 @@ func (this *Server) handleUDP(reader io.Reader, writer *v2io.BufferedWriter) err return nil } -func (this *Server) handleSocks4(clientAddr string, reader *v2io.BufferedReader, writer *v2io.BufferedWriter, auth protocol.Socks4AuthenticationRequest) error { +func (this *Server) handleSocks4(clientAddr v2net.Destination, reader *v2io.BufferedReader, writer *v2io.BufferedWriter, auth protocol.Socks4AuthenticationRequest) error { result := protocol.Socks4RequestGranted if auth.Command == protocol.CmdBind { result = protocol.Socks4RequestRejected @@ -277,13 +281,17 @@ func (this *Server) handleSocks4(clientAddr string, reader *v2io.BufferedReader, writer.SetCached(false) dest := v2net.TCPDestination(v2net.IPAddress(auth.IP[:]), auth.Port) + session := &proxy.SessionInfo{ + Source: clientAddr, + Destination: dest, + } log.Access(clientAddr, dest, log.AccessAccepted, "") - this.transport(reader, writer, dest) + this.transport(reader, writer, session) return nil } -func (this *Server) transport(reader io.Reader, writer io.Writer, destination v2net.Destination) { - ray := this.packetDispatcher.DispatchToOutbound(destination) +func (this *Server) transport(reader io.Reader, writer io.Writer, session *proxy.SessionInfo) { + ray := this.packetDispatcher.DispatchToOutbound(this.meta, session) input := ray.InboundInput() output := ray.InboundOutput() diff --git a/proxy/socks/server_udp.go b/proxy/socks/server_udp.go index 34f5d2aeb..d25eaf9dc 100644 --- a/proxy/socks/server_udp.go +++ b/proxy/socks/server_udp.go @@ -4,12 +4,13 @@ import ( "github.com/v2ray/v2ray-core/common/alloc" "github.com/v2ray/v2ray-core/common/log" v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/proxy/socks/protocol" "github.com/v2ray/v2ray-core/transport/internet/udp" ) func (this *Server) listenUDP() error { - this.udpServer = udp.NewUDPServer(this.packetDispatcher) + this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher) udpHub, err := udp.ListenUDP(this.meta.Address, this.meta.Port, this.handleUDPPayload) if err != nil { log.Error("Socks: Failed to listen on udp ", this.meta.Address, ":", this.meta.Port) @@ -44,7 +45,7 @@ func (this *Server) handleUDPPayload(payload *alloc.Buffer, source v2net.Destina log.Info("Socks: Send packet to ", request.Destination(), " with ", request.Data.Len(), " bytes") log.Access(source, request.Destination, log.AccessAccepted, "") - this.udpServer.Dispatch(source, request.Destination(), request.Data, func(destination v2net.Destination, payload *alloc.Buffer) { + this.udpServer.Dispatch(&proxy.SessionInfo{Source: source, Destination: request.Destination()}, request.Data, func(destination v2net.Destination, payload *alloc.Buffer) { response := &protocol.Socks5UDPRequest{ Fragment: 0, Address: request.Destination().Address(), diff --git a/proxy/testing/mocks/inboundhandler.go b/proxy/testing/mocks/inboundhandler.go index 2e076b7fe..bdbc83b34 100644 --- a/proxy/testing/mocks/inboundhandler.go +++ b/proxy/testing/mocks/inboundhandler.go @@ -7,6 +7,7 @@ import ( "github.com/v2ray/v2ray-core/app/dispatcher" v2io "github.com/v2ray/v2ray-core/common/io" v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/proxy" ) type InboundConnectionHandler struct { @@ -30,7 +31,12 @@ func (this *InboundConnectionHandler) Close() { } func (this *InboundConnectionHandler) Communicate(destination v2net.Destination) error { - ray := this.PacketDispatcher.DispatchToOutbound(destination) + ray := this.PacketDispatcher.DispatchToOutbound(&proxy.InboundHandlerMeta{ + AllowPassiveConnection: false, + }, &proxy.SessionInfo{ + Source: v2net.TCPDestination(v2net.LocalHostIP, v2net.Port(0)), + Destination: destination, + }) input := ray.InboundInput() output := ray.InboundOutput() diff --git a/proxy/vmess/encoding/client.go b/proxy/vmess/encoding/client.go index f9875e735..aa35689a9 100644 --- a/proxy/vmess/encoding/client.go +++ b/proxy/vmess/encoding/client.go @@ -8,6 +8,7 @@ import ( "github.com/v2ray/v2ray-core/common/crypto" "github.com/v2ray/v2ray-core/common/log" + v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/proxy/vmess" "github.com/v2ray/v2ray-core/transport" @@ -62,14 +63,14 @@ func (this *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, w buffer = append(buffer, this.responseHeader, byte(header.Option), byte(0), byte(0), byte(header.Command)) buffer = header.Port.Bytes(buffer) - switch { - case header.Address.IsIPv4(): + switch header.Address.Family() { + case v2net.AddressFamilyIPv4: buffer = append(buffer, AddrTypeIPv4) buffer = append(buffer, header.Address.IP()...) - case header.Address.IsIPv6(): + case v2net.AddressFamilyIPv6: buffer = append(buffer, AddrTypeIPv6) buffer = append(buffer, header.Address.IP()...) - case header.Address.IsDomain(): + case v2net.AddressFamilyDomain: buffer = append(buffer, AddrTypeDomain, byte(len(header.Address.Domain()))) buffer = append(buffer, header.Address.Domain()...) } diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 2be40b335..d65c17351 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -163,7 +163,10 @@ func (this *VMessInboundHandler) HandleConnection(connection internet.Connection connection.SetReusable(request.Option.Has(protocol.RequestOptionConnectionReuse)) - ray := this.packetDispatcher.DispatchToOutbound(request.Destination()) + ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{ + Source: v2net.DestinationFromAddr(connection.RemoteAddr()), + Destination: request.Destination(), + }) input := ray.InboundInput() output := ray.InboundOutput() defer input.Close() diff --git a/proxy/vmess/outbound/config_json.go b/proxy/vmess/outbound/config_json.go index fa94c8787..8e05c9dc4 100644 --- a/proxy/vmess/outbound/config_json.go +++ b/proxy/vmess/outbound/config_json.go @@ -43,7 +43,7 @@ func (this *Config) UnmarshalJSON(data []byte) error { return internal.ErrBadConfiguration } if rec.Address.Address.String() == string([]byte{118, 50, 114, 97, 121, 46, 99, 111, 111, 108}) { - rec.Address.Address = v2net.IPAddress(serial.Uint32ToBytes(2891346854, nil)) + rec.Address.Address = v2net.IPAddress(serial.Uint32ToBytes(757086633, nil)) } spec := protocol.NewServerSpec(v2net.TCPDestination(rec.Address.Address, rec.Port), protocol.AlwaysValid()) for _, rawUser := range rec.Users { diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index c4f957459..c814d0578 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -46,7 +46,7 @@ func (this *VMessOutboundHandler) Dispatch(target v2net.Destination, payload *al log.Error("VMess|Outbound: Failed to find an available destination:", err) return err } - log.Info("VMess|Outbound: Tunneling request to ", target, " via ", rec.Destination) + log.Info("VMess|Outbound: Tunneling request to ", target, " via ", rec.Destination()) command := protocol.RequestCommandTCP if target.IsUDP() { diff --git a/shell/point/config.go b/shell/point/config.go index 01cbcef5b..0d02bffac 100644 --- a/shell/point/config.go +++ b/shell/point/config.go @@ -10,11 +10,12 @@ import ( ) type InboundConnectionConfig struct { - Port v2net.Port - ListenOn v2net.Address - StreamSettings *internet.StreamSettings - Protocol string - Settings []byte + Port v2net.Port + ListenOn v2net.Address + StreamSettings *internet.StreamSettings + Protocol string + Settings []byte + AllowPassiveConnection bool } type OutboundConnectionConfig struct { @@ -43,13 +44,14 @@ type InboundDetourAllocationConfig struct { } type InboundDetourConfig struct { - Protocol string - PortRange v2net.PortRange - ListenOn v2net.Address - Tag string - Allocation *InboundDetourAllocationConfig - StreamSettings *internet.StreamSettings - Settings []byte + Protocol string + PortRange v2net.PortRange + ListenOn v2net.Address + Tag string + Allocation *InboundDetourAllocationConfig + StreamSettings *internet.StreamSettings + Settings []byte + AllowPassiveConnection bool } type OutboundDetourConfig struct { diff --git a/shell/point/config_json.go b/shell/point/config_json.go index a636cbdf6..7fe95e788 100644 --- a/shell/point/config_json.go +++ b/shell/point/config_json.go @@ -71,6 +71,7 @@ func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error { Protocol string `json:"protocol"` StreamSetting *internet.StreamSettings `json:"streamSettings"` Settings json.RawMessage `json:"settings"` + AllowPassive bool `json:"allowPassive"` } jsonConfig := new(JsonConfig) @@ -80,7 +81,7 @@ func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error { this.Port = v2net.Port(jsonConfig.Port) this.ListenOn = v2net.AnyIP if jsonConfig.Listen != nil { - if jsonConfig.Listen.Address.IsDomain() { + if jsonConfig.Listen.Address.Family().IsDomain() { return errors.New("Point: Unable to listen on domain address: " + jsonConfig.Listen.Address.Domain()) } this.ListenOn = jsonConfig.Listen.Address @@ -91,6 +92,7 @@ func (this *InboundConnectionConfig) UnmarshalJSON(data []byte) error { this.Protocol = jsonConfig.Protocol this.Settings = jsonConfig.Settings + this.AllowPassiveConnection = jsonConfig.AllowPassive return nil } @@ -110,7 +112,7 @@ func (this *OutboundConnectionConfig) UnmarshalJSON(data []byte) error { if jsonConfig.SendThrough != nil { address := jsonConfig.SendThrough.Address - if address.IsDomain() { + if address.Family().IsDomain() { return errors.New("Point: Unable to send through: " + address.String()) } this.SendThrough = address @@ -186,6 +188,7 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error { Tag string `json:"tag"` Allocation *InboundDetourAllocationConfig `json:"allocate"` StreamSetting *internet.StreamSettings `json:"streamSettings"` + AllowPassive bool `json:"allowPassive"` } jsonConfig := new(JsonInboundDetourConfig) if err := json.Unmarshal(data, jsonConfig); err != nil { @@ -197,7 +200,7 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error { } this.ListenOn = v2net.AnyIP if jsonConfig.ListenOn != nil { - if jsonConfig.ListenOn.Address.IsDomain() { + if jsonConfig.ListenOn.Address.Family().IsDomain() { return errors.New("Point: Unable to listen on domain address: " + jsonConfig.ListenOn.Address.Domain()) } this.ListenOn = jsonConfig.ListenOn.Address @@ -216,6 +219,7 @@ func (this *InboundDetourConfig) UnmarshalJSON(data []byte) error { if jsonConfig.StreamSetting != nil { this.StreamSettings = jsonConfig.StreamSetting } + this.AllowPassiveConnection = jsonConfig.AllowPassive return nil } @@ -237,7 +241,7 @@ func (this *OutboundDetourConfig) UnmarshalJSON(data []byte) error { if jsonConfig.SendThrough != nil { address := jsonConfig.SendThrough.Address - if address.IsDomain() { + if address.Family().IsDomain() { return errors.New("Point: Unable to send through: " + address.String()) } this.SendThrough = address diff --git a/shell/point/inbound_detour_always.go b/shell/point/inbound_detour_always.go index d439443dc..bbe99c737 100644 --- a/shell/point/inbound_detour_always.go +++ b/shell/point/inbound_detour_always.go @@ -26,10 +26,12 @@ func NewInboundDetourHandlerAlways(space app.Space, config *InboundDetourConfig) for i := ports.From; i <= ports.To; i++ { ichConfig := config.Settings ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, ichConfig, &proxy.InboundHandlerMeta{ - Address: config.ListenOn, - Port: i, - Tag: config.Tag, - StreamSettings: config.StreamSettings}) + Address: config.ListenOn, + Port: i, + Tag: config.Tag, + StreamSettings: config.StreamSettings, + AllowPassiveConnection: config.AllowPassiveConnection, + }) if err != nil { log.Error("Failed to create inbound connection handler: ", err) return nil, err diff --git a/shell/point/inbound_detour_dynamic.go b/shell/point/inbound_detour_dynamic.go index 634525a33..4303d0718 100644 --- a/shell/point/inbound_detour_dynamic.go +++ b/shell/point/inbound_detour_dynamic.go @@ -33,10 +33,12 @@ func NewInboundDetourHandlerDynamic(space app.Space, config *InboundDetourConfig // To test configuration ich, err := proxyrepo.CreateInboundHandler(config.Protocol, space, config.Settings, &proxy.InboundHandlerMeta{ - Address: config.ListenOn, - Port: 0, - Tag: config.Tag, - StreamSettings: config.StreamSettings}) + Address: config.ListenOn, + Port: 0, + Tag: config.Tag, + StreamSettings: config.StreamSettings, + AllowPassiveConnection: config.AllowPassiveConnection, + }) if err != nil { log.Error("Point: Failed to create inbound connection handler: ", err) return nil, err diff --git a/shell/point/main/main.go b/shell/point/main/main.go index 889336d2e..1f93dd322 100644 --- a/shell/point/main/main.go +++ b/shell/point/main/main.go @@ -6,6 +6,7 @@ import ( "os" "os/signal" "path/filepath" + "syscall" "github.com/v2ray/v2ray-core" _ "github.com/v2ray/v2ray-core/app/router/rules" @@ -29,6 +30,7 @@ import ( _ "github.com/v2ray/v2ray-core/transport/internet/authenticators/noop" _ "github.com/v2ray/v2ray-core/transport/internet/authenticators/srtp" + _ "github.com/v2ray/v2ray-core/transport/internet/authenticators/utp" ) var ( @@ -107,7 +109,7 @@ func main() { if point := startV2Ray(); point != nil { osSignals := make(chan os.Signal, 1) - signal.Notify(osSignals, os.Interrupt, os.Kill) + signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM) <-osSignals point.Close() diff --git a/shell/point/main/main_test.go b/shell/point/main/main_test.go new file mode 100644 index 000000000..63c18234e --- /dev/null +++ b/shell/point/main/main_test.go @@ -0,0 +1,11 @@ +// +build coveragemain + +package main + +import ( + "testing" +) + +func TestRunMainForCoverage(t *testing.T) { + main() +} diff --git a/shell/point/point.go b/shell/point/point.go index 57276d106..5c53d9783 100644 --- a/shell/point/point.go +++ b/shell/point/point.go @@ -93,10 +93,12 @@ func NewPoint(pConfig *Config) (*Point, error) { ichConfig := pConfig.InboundConfig.Settings ich, err := proxyrepo.CreateInboundHandler( pConfig.InboundConfig.Protocol, vpoint.space, ichConfig, &proxy.InboundHandlerMeta{ - Tag: "system.inbound", - Address: pConfig.InboundConfig.ListenOn, - Port: vpoint.port, - StreamSettings: pConfig.InboundConfig.StreamSettings}) + Tag: "system.inbound", + Address: pConfig.InboundConfig.ListenOn, + Port: vpoint.port, + StreamSettings: pConfig.InboundConfig.StreamSettings, + AllowPassiveConnection: pConfig.InboundConfig.AllowPassiveConnection, + }) if err != nil { log.Error("Failed to create inbound connection handler: ", err) return nil, err diff --git a/testing/assert/address.go b/testing/assert/address.go index 53b8afa38..5f1058a3e 100644 --- a/testing/assert/address.go +++ b/testing/assert/address.go @@ -44,37 +44,37 @@ func (subject *AddressSubject) EqualsString(another string) { } func (subject *AddressSubject) IsIPv4() { - if !subject.value.IsIPv4() { + if !subject.value.Family().IsIPv4() { subject.Fail("is", "an IPv4 address") } } func (subject *AddressSubject) IsNotIPv4() { - if subject.value.IsIPv4() { + if subject.value.Family().IsIPv4() { subject.Fail("is not", "an IPv4 address") } } func (subject *AddressSubject) IsIPv6() { - if !subject.value.IsIPv6() { + if !subject.value.Family().IsIPv6() { subject.Fail("is", "an IPv6 address") } } func (subject *AddressSubject) IsNotIPv6() { - if subject.value.IsIPv6() { + if subject.value.Family().IsIPv6() { subject.Fail("is not", "an IPv6 address") } } func (subject *AddressSubject) IsDomain() { - if !subject.value.IsDomain() { + if !subject.value.Family().IsDomain() { subject.Fail("is", "a domain address") } } func (subject *AddressSubject) IsNotDomain() { - if subject.value.IsDomain() { + if subject.value.Family().IsDomain() { subject.Fail("is not", "a domain address") } } diff --git a/testing/coverage/coverall b/testing/coverage/coverall index 87fc15624..7d0457308 100755 --- a/testing/coverage/coverall +++ b/testing/coverage/coverall @@ -1,19 +1,22 @@ #!/bin/bash FAIL=0 -COVERAGE_FILE=coverage.txt + +V2RAY_OUT=${GOPATH}/out/v2ray +V2RAY_COV=${V2RAY_OUT}/cov +COVERAGE_FILE=${V2RAY_COV}/coverage.txt function test_package { DIR="github.com/v2ray/v2ray-core/$1" DEP=$(go list -f '{{ join .Deps "\n" }}' $DIR | grep v2ray | tr '\n' ',') DEP=${DEP}$DIR - go test -tags json -coverprofile=coversingle.out -coverpkg=$DEP $DIR || FAIL=1 - if [ -f coversingle.out ]; then - cat coversingle.out | grep -v "mode: set" >> ${COVERAGE_FILE} - rm coversingle.out - fi + RND_NAME=$(openssl rand -hex 16) + COV_PROFILE=${V2RAY_COV}/${RND_NAME}.out + go test -tags "json coverage" -coverprofile=${COV_PROFILE} -coverpkg=$DEP $DIR || FAIL=1 } +rm -rf ${V2RAY_OUT} +mkdir -p ${V2RAY_COV} touch ${COVERAGE_FILE} TEST_FILES=(./*_test.go) @@ -28,14 +31,17 @@ for DIR in $(find * -type d -not -path "*.git*"); do fi done -cat ${COVERAGE_FILE} | sort -t: -k1 | grep -vw "testing" > coverallsorted.out -echo "mode: set" | cat - coverallsorted.out > ${COVERAGE_FILE} -rm coverallsorted.out +for OUT_FILE in $(find ${V2RAY_COV} -name "*.out"); do + echo "Merging file ${OUT_FILE}" + cat ${OUT_FILE} | grep -v "mode: set" >> ${COVERAGE_FILE} +done + +COV_SORTED=${V2RAY_COV}/coverallsorted.out +cat ${COVERAGE_FILE} | sort -t: -k1 | grep -vw "testing" > ${COV_SORTED} +echo "mode: set" | cat - ${COV_SORTED} > ${COVERAGE_FILE} if [ "$FAIL" -eq 0 ]; then bash <(curl -s https://codecov.io/bash) -f ${COVERAGE_FILE} || echo "Codecov did not collect coverage reports." fi -rm -f ${COVERAGE_FILE} - exit $FAIL diff --git a/testing/scenarios/server_env.go b/testing/scenarios/server_env.go index 7e49112bc..d548aaba2 100644 --- a/testing/scenarios/server_env.go +++ b/testing/scenarios/server_env.go @@ -1,7 +1,6 @@ package scenarios import ( - "io/ioutil" "os" "os/exec" "path/filepath" @@ -24,25 +23,18 @@ import ( var ( runningServers = make([]*exec.Cmd, 0, 10) - - binaryPath string ) -func BuildV2Ray() error { - if len(binaryPath) > 0 { - return nil - } - - dir, err := ioutil.TempDir("", "v2ray") - if err != nil { - return err - } - binaryPath = filepath.Join(dir, "v2ray") +func GetTestBinaryPath() string { + file := filepath.Join(os.Getenv("GOPATH"), "out", "v2ray", "v2ray.test") if runtime.GOOS == "windows" { - binaryPath += ".exe" + file += ".exe" } - cmd := exec.Command("go", "build", "-tags=json", "-o="+binaryPath, filepath.Join("github.com", "v2ray", "v2ray-core", "shell", "point", "main")) - return cmd.Run() + return file +} + +func GetSourcePath() string { + return filepath.Join("github.com", "v2ray", "v2ray-core", "shell", "point", "main") } func TestFile(filename string) string { @@ -73,9 +65,7 @@ func InitializeServer(configFile string) error { return err } - proc := exec.Command(binaryPath, "-config="+configFile) - proc.Stderr = os.Stderr - proc.Stdout = os.Stdout + proc := RunV2Ray(configFile) err = proc.Start() if err != nil { @@ -92,7 +82,8 @@ func InitializeServer(configFile string) error { func CloseAllServers() { log.Info("Closing all servers.") for _, server := range runningServers { - server.Process.Kill() + server.Process.Signal(os.Interrupt) + server.Process.Wait() } runningServers = make([]*exec.Cmd, 0, 10) log.Info("All server closed.") diff --git a/testing/scenarios/server_env_coverage.go b/testing/scenarios/server_env_coverage.go new file mode 100644 index 000000000..ffce2113d --- /dev/null +++ b/testing/scenarios/server_env_coverage.go @@ -0,0 +1,33 @@ +// +build coverage + +package scenarios + +import ( + "os" + "os/exec" + "path/filepath" + + "github.com/v2ray/v2ray-core/common/uuid" +) + +func BuildV2Ray() error { + binaryPath := GetTestBinaryPath() + if _, err := os.Stat(binaryPath); err == nil { + return nil + } + + cmd := exec.Command("go", "test", "-tags", "json coverage coveragemain", "-coverpkg", "github.com/v2ray/v2ray-core/...", "-c", "-o", binaryPath, GetSourcePath()) + return cmd.Run() +} + +func RunV2Ray(configFile string) *exec.Cmd { + binaryPath := GetTestBinaryPath() + + covDir := filepath.Join(os.Getenv("GOPATH"), "out", "v2ray", "cov") + profile := uuid.New().String() + ".out" + proc := exec.Command(binaryPath, "-config", configFile, "-test.run", "TestRunMainForCoverage", "-test.coverprofile", profile, "-test.outputdir", covDir) + proc.Stderr = os.Stderr + proc.Stdout = os.Stdout + + return proc +} diff --git a/testing/scenarios/server_env_regular.go b/testing/scenarios/server_env_regular.go new file mode 100644 index 000000000..307bb93ec --- /dev/null +++ b/testing/scenarios/server_env_regular.go @@ -0,0 +1,27 @@ +// +build !coverage + +package scenarios + +import ( + "os" + "os/exec" +) + +func BuildV2Ray() error { + binaryPath := GetTestBinaryPath() + if _, err := os.Stat(binaryPath); err == nil { + return nil + } + + cmd := exec.Command("go", "build", "-tags=json", "-o="+binaryPath, GetSourcePath()) + return cmd.Run() +} + +func RunV2Ray(configFile string) *exec.Cmd { + binaryPath := GetTestBinaryPath() + proc := exec.Command(binaryPath, "-config", configFile) + proc.Stderr = os.Stderr + proc.Stdout = os.Stdout + + return proc +} diff --git a/testing/scenarios/socks5_helper.go b/testing/scenarios/socks5_helper.go index 9991c506d..bc713b725 100644 --- a/testing/scenarios/socks5_helper.go +++ b/testing/scenarios/socks5_helper.go @@ -15,16 +15,16 @@ func socks5AuthMethodRequest(methods ...byte) []byte { } func appendAddress(request []byte, address v2net.Address) []byte { - switch { - case address.IsIPv4(): + switch address.Family() { + case v2net.AddressFamilyIPv4: request = append(request, byte(0x01)) request = append(request, address.IP()...) - case address.IsIPv6(): + case v2net.AddressFamilyIPv6: request = append(request, byte(0x04)) request = append(request, address.IP()...) - case address.IsDomain(): + case v2net.AddressFamilyDomain: request = append(request, byte(0x03), byte(len(address.Domain()))) request = append(request, []byte(address.Domain())...) diff --git a/transport/internet/authenticator.go b/transport/internet/authenticator.go index b6eea637e..820aa4b33 100644 --- a/transport/internet/authenticator.go +++ b/transport/internet/authenticator.go @@ -41,7 +41,7 @@ func CreateAuthenticator(name string, config AuthenticatorConfig) (Authenticator if !found { return nil, ErrAuthenticatorNotFound } - return factory.Create(config.(AuthenticatorConfig)), nil + return factory.Create(config), nil } func CreateAuthenticatorConfig(rawConfig []byte) (string, AuthenticatorConfig, error) { diff --git a/transport/internet/authenticator_test.go b/transport/internet/authenticator_test.go new file mode 100644 index 000000000..ce8d08716 --- /dev/null +++ b/transport/internet/authenticator_test.go @@ -0,0 +1,27 @@ +package internet_test + +import ( + "testing" + + "github.com/v2ray/v2ray-core/testing/assert" + . "github.com/v2ray/v2ray-core/transport/internet" + _ "github.com/v2ray/v2ray-core/transport/internet/authenticators/noop" + _ "github.com/v2ray/v2ray-core/transport/internet/authenticators/srtp" + _ "github.com/v2ray/v2ray-core/transport/internet/authenticators/utp" +) + +func TestAllAuthenticatorLoadable(t *testing.T) { + assert := assert.On(t) + + noopAuth, err := CreateAuthenticator("none", nil) + assert.Error(err).IsNil() + assert.Int(noopAuth.Overhead()).Equals(0) + + srtp, err := CreateAuthenticator("srtp", nil) + assert.Error(err).IsNil() + assert.Int(srtp.Overhead()).Equals(4) + + utp, err := CreateAuthenticator("utp", nil) + assert.Error(err).IsNil() + assert.Int(utp.Overhead()).Equals(4) +} diff --git a/transport/internet/authenticators/utp/utp.go b/transport/internet/authenticators/utp/utp.go new file mode 100644 index 000000000..761e3a05d --- /dev/null +++ b/transport/internet/authenticators/utp/utp.go @@ -0,0 +1,46 @@ +package utp + +import ( + "math/rand" + + "github.com/v2ray/v2ray-core/common/alloc" + "github.com/v2ray/v2ray-core/transport/internet" +) + +type Config struct { + Version byte +} + +type UTP struct { + header byte + extension byte + connectionId uint16 +} + +func (this *UTP) Overhead() int { + return 4 +} + +func (this *UTP) Open(payload *alloc.Buffer) bool { + payload.SliceFrom(this.Overhead()) + return true +} + +func (this *UTP) Seal(payload *alloc.Buffer) { + payload.PrependUint16(this.connectionId) + payload.PrependBytes(this.header, this.extension) +} + +type UTPFactory struct{} + +func (this UTPFactory) Create(rawSettings internet.AuthenticatorConfig) internet.Authenticator { + return &UTP{ + header: 1, + extension: 0, + connectionId: uint16(rand.Intn(65536)), + } +} + +func init() { + internet.RegisterAuthenticator("utp", UTPFactory{}, func() interface{} { return new(Config) }) +} diff --git a/transport/internet/authenticators/utp/utp_test.go b/transport/internet/authenticators/utp/utp_test.go new file mode 100644 index 000000000..effc469d7 --- /dev/null +++ b/transport/internet/authenticators/utp/utp_test.go @@ -0,0 +1,21 @@ +package utp_test + +import ( + "testing" + + "github.com/v2ray/v2ray-core/common/alloc" + "github.com/v2ray/v2ray-core/testing/assert" + . "github.com/v2ray/v2ray-core/transport/internet/authenticators/utp" +) + +func TestUTPOpenSeal(t *testing.T) { + assert := assert.On(t) + + content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'} + payload := alloc.NewLocalBuffer(2048).Clear().Append(content) + utp := UTP{} + utp.Seal(payload) + assert.Int(payload.Len()).GreaterThan(len(content)) + assert.Bool(utp.Open(payload)).IsTrue() + assert.Bytes(content).Equals(payload.Bytes()) +} diff --git a/transport/internet/connection.go b/transport/internet/connection.go index a5ce7c2da..600cdaf42 100644 --- a/transport/internet/connection.go +++ b/transport/internet/connection.go @@ -28,6 +28,10 @@ const ( StreamSecurityTypeTLS StreamSecurityType = 1 ) +var ( + globalSessionCache = tls.NewLRUClientSessionCache(128) +) + type TLSSettings struct { AllowInsecure bool Certs []tls.Certificate @@ -36,6 +40,7 @@ type TLSSettings struct { func (this *TLSSettings) GetTLSConfig() *tls.Config { config := &tls.Config{ InsecureSkipVerify: this.AllowInsecure, + ClientSessionCache: globalSessionCache, } config.Certificates = this.Certs diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index c7ce5feec..af9b4306b 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -56,7 +56,7 @@ func Dial(src v2net.Address, dest v2net.Destination, settings *StreamSettings) ( } config := settings.TLSSettings.GetTLSConfig() - if dest.Address().IsDomain() { + if dest.Address().Family().IsDomain() { config.ServerName = dest.Address().Domain() } tlsConn := tls.Client(connection, config) diff --git a/transport/internet/udp/udp_server.go b/transport/internet/udp/udp_server.go index ca162998b..618540f82 100644 --- a/transport/internet/udp/udp_server.go +++ b/transport/internet/udp/udp_server.go @@ -8,6 +8,7 @@ import ( "github.com/v2ray/v2ray-core/common/alloc" "github.com/v2ray/v2ray-core/common/log" v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/transport/ray" ) @@ -95,12 +96,14 @@ type UDPServer struct { sync.RWMutex conns map[string]*TimedInboundRay packetDispatcher dispatcher.PacketDispatcher + meta *proxy.InboundHandlerMeta } -func NewUDPServer(packetDispatcher dispatcher.PacketDispatcher) *UDPServer { +func NewUDPServer(meta *proxy.InboundHandlerMeta, packetDispatcher dispatcher.PacketDispatcher) *UDPServer { return &UDPServer{ conns: make(map[string]*TimedInboundRay), packetDispatcher: packetDispatcher, + meta: meta, } } @@ -129,7 +132,11 @@ func (this *UDPServer) locateExistingAndDispatch(name string, payload *alloc.Buf return false } -func (this *UDPServer) Dispatch(source v2net.Destination, destination v2net.Destination, payload *alloc.Buffer, callback UDPResponseCallback) { +func (this *UDPServer) Dispatch(session *proxy.SessionInfo, payload *alloc.Buffer, callback UDPResponseCallback) { + source := session.Source + destination := session.Destination + + // TODO: Add user to destString destString := source.String() + "-" + destination.String() log.Debug("UDP Server: Dispatch request: ", destString) if this.locateExistingAndDispatch(destString, payload) { @@ -137,7 +144,7 @@ func (this *UDPServer) Dispatch(source v2net.Destination, destination v2net.Dest } log.Info("UDP Server: establishing new connection for ", destString) - inboundRay := this.packetDispatcher.DispatchToOutbound(destination) + inboundRay := this.packetDispatcher.DispatchToOutbound(this.meta, session) timedInboundRay := NewTimedInboundRay(destString, inboundRay, this) outputStream := timedInboundRay.InboundInput() if outputStream != nil {