Browse Source

Merge branch 'master' into websockwt

Shelikhoo 9 years ago
parent
commit
7259b3363b
47 changed files with 437 additions and 184 deletions
  1. 3 5
      .travis.yml
  2. 2 2
      app/dispatcher/dispatcher.go
  3. 8 2
      app/dispatcher/impl/default.go
  4. 4 3
      app/dispatcher/testing/dispatcher.go
  5. 1 1
      app/dns/config_json.go
  6. 7 4
      app/dns/nameserver.go
  7. 1 1
      app/dns/server.go
  8. 4 4
      app/router/rules/condition.go
  9. 1 1
      app/router/rules/router.go
  10. 36 34
      common/net/address.go
  11. 4 4
      common/net/address_json_test.go
  12. 15 0
      common/net/destination.go
  13. 1 1
      core.go
  14. 2 0
      proxy/blackhole/config_json_test.go
  15. 7 3
      proxy/dokodemo/dokodemo.go
  16. 2 2
      proxy/freedom/freedom.go
  17. 10 6
      proxy/http/server.go
  18. 12 4
      proxy/proxy.go
  19. 10 7
      proxy/shadowsocks/server.go
  20. 4 4
      proxy/socks/protocol/udp.go
  21. 19 11
      proxy/socks/server.go
  22. 3 2
      proxy/socks/server_udp.go
  23. 7 1
      proxy/testing/mocks/inboundhandler.go
  24. 5 4
      proxy/vmess/encoding/client.go
  25. 4 1
      proxy/vmess/inbound/inbound.go
  26. 1 1
      proxy/vmess/outbound/config_json.go
  27. 1 1
      proxy/vmess/outbound/outbound.go
  28. 14 12
      shell/point/config.go
  29. 8 4
      shell/point/config_json.go
  30. 6 4
      shell/point/inbound_detour_always.go
  31. 6 4
      shell/point/inbound_detour_dynamic.go
  32. 3 1
      shell/point/main/main.go
  33. 11 0
      shell/point/main/main_test.go
  34. 6 4
      shell/point/point.go
  35. 6 6
      testing/assert/address.go
  36. 17 11
      testing/coverage/coverall
  37. 11 20
      testing/scenarios/server_env.go
  38. 33 0
      testing/scenarios/server_env_coverage.go
  39. 27 0
      testing/scenarios/server_env_regular.go
  40. 4 4
      testing/scenarios/socks5_helper.go
  41. 1 1
      transport/internet/authenticator.go
  42. 27 0
      transport/internet/authenticator_test.go
  43. 46 0
      transport/internet/authenticators/utp/utp.go
  44. 21 0
      transport/internet/authenticators/utp/utp_test.go
  45. 5 0
      transport/internet/connection.go
  46. 1 1
      transport/internet/dialer.go
  47. 10 3
      transport/internet/udp/udp_server.go

+ 3 - 5
.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/...
   

+ 2 - 2
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
 }

+ 8 - 2
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
 }

+ 4 - 3
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
 }

+ 1 - 1
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()

+ 7 - 4
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 {

+ 1 - 1
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)

+ 4 - 4
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())

+ 1 - 1
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 {

+ 36 - 34
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 {

+ 4 - 4
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")
 }
 

+ 15 - 0
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}

+ 1 - 1
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."

+ 2 - 0
proxy/blackhole/config_json_test.go

@@ -1,3 +1,5 @@
+// +build json
+
 package blackhole_test
 
 import (

+ 7 - 3
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

+ 2 - 2
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 {

+ 10 - 6
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()
 

+ 12 - 4
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 {

+ 10 - 7
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

+ 4 - 4
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())

+ 19 - 11
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()
 

+ 3 - 2
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(),

+ 7 - 1
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()

+ 5 - 4
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()...)
 	}

+ 4 - 1
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()

+ 1 - 1
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 {

+ 1 - 1
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() {

+ 14 - 12
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 {

+ 8 - 4
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

+ 6 - 4
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

+ 6 - 4
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

+ 3 - 1
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()

+ 11 - 0
shell/point/main/main_test.go

@@ -0,0 +1,11 @@
+// +build coveragemain
+
+package main
+
+import (
+	"testing"
+)
+
+func TestRunMainForCoverage(t *testing.T) {
+	main()
+}

+ 6 - 4
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

+ 6 - 6
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")
 	}
 }

+ 17 - 11
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

+ 11 - 20
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.")

+ 33 - 0
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
+}

+ 27 - 0
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
+}

+ 4 - 4
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())...)
 

+ 1 - 1
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) {

+ 27 - 0
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)
+}

+ 46 - 0
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) })
+}

+ 21 - 0
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())
+}

+ 5 - 0
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

+ 1 - 1
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)

+ 10 - 3
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 {