From 0ea2678e72ed61298cb14b8611d9b8da8c5d7cc6 Mon Sep 17 00:00:00 2001 From: v2ray Date: Sun, 22 May 2016 22:30:21 +0200 Subject: [PATCH] Allow freedom to consume DNS settings --- proxy/dokodemo/dokodemo_test.go | 4 +-- proxy/freedom/config.go | 8 +++++ proxy/freedom/config_json.go | 25 +++++++++++++- proxy/freedom/freedom.go | 58 +++++++++++++++++++++++++++++++++ proxy/freedom/freedom_test.go | 40 +++++++++++++++++++++-- proxy/freedom/freedomfactory.go | 14 -------- 6 files changed, 130 insertions(+), 19 deletions(-) delete mode 100644 proxy/freedom/freedomfactory.go diff --git a/proxy/dokodemo/dokodemo_test.go b/proxy/dokodemo/dokodemo_test.go index fa9ef3380..7fa760115 100644 --- a/proxy/dokodemo/dokodemo_test.go +++ b/proxy/dokodemo/dokodemo_test.go @@ -39,7 +39,7 @@ func TestDokodemoTCP(t *testing.T) { space := app.NewSpace() space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) ohm := proxyman.NewDefaultOutboundHandlerManager() - ohm.SetDefaultHandler(&freedom.FreedomConnection{}) + ohm.SetDefaultHandler(freedom.NewFreedomConnection(&freedom.Config{}, space)) space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, ohm) data2Send := "Data to be sent to remote." @@ -97,7 +97,7 @@ func TestDokodemoUDP(t *testing.T) { space := app.NewSpace() space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) ohm := proxyman.NewDefaultOutboundHandlerManager() - ohm.SetDefaultHandler(&freedom.FreedomConnection{}) + ohm.SetDefaultHandler(freedom.NewFreedomConnection(&freedom.Config{}, space)) space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, ohm) data2Send := "Data to be sent to remote." diff --git a/proxy/freedom/config.go b/proxy/freedom/config.go index a61f352fd..416719df0 100644 --- a/proxy/freedom/config.go +++ b/proxy/freedom/config.go @@ -1,4 +1,12 @@ package freedom +type DomainStrategy int + +const ( + DomainStrategyAsIs = DomainStrategy(0) + DomainStrategyUseIP = DomainStrategy(1) +) + type Config struct { + DomainStrategy DomainStrategy } diff --git a/proxy/freedom/config_json.go b/proxy/freedom/config_json.go index 461aba499..5ce1cc714 100644 --- a/proxy/freedom/config_json.go +++ b/proxy/freedom/config_json.go @@ -3,12 +3,35 @@ package freedom import ( + "encoding/json" + + "github.com/v2ray/v2ray-core/common/serial" "github.com/v2ray/v2ray-core/proxy/internal/config" ) +func (this *Config) UnmarshalJSON(data []byte) error { + type JsonConfig struct { + DomainStrategy string `json:"domainStrategy"` + } + jsonConfig := new(JsonConfig) + if err := json.Unmarshal(data, jsonConfig); err != nil { + return err + } + this.DomainStrategy = DomainStrategyAsIs + domainStrategy := serial.StringLiteral(jsonConfig.DomainStrategy).ToLower() + if domainStrategy.String() == "useip" { + this.DomainStrategy = DomainStrategyUseIP + } + return nil +} + func init() { config.RegisterOutboundConfig("freedom", func(data []byte) (interface{}, error) { - return new(Config), nil + c := new(Config) + if err := json.Unmarshal(data, c); err != nil { + return nil, err + } + return c, nil }) } diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index ee0814287..c697e6cc2 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -5,16 +5,64 @@ import ( "net" "sync" + "github.com/v2ray/v2ray-core/app" + "github.com/v2ray/v2ray-core/app/dns" "github.com/v2ray/v2ray-core/common/alloc" + "github.com/v2ray/v2ray-core/common/dice" v2io "github.com/v2ray/v2ray-core/common/io" "github.com/v2ray/v2ray-core/common/log" v2net "github.com/v2ray/v2ray-core/common/net" "github.com/v2ray/v2ray-core/common/retry" + "github.com/v2ray/v2ray-core/proxy" + "github.com/v2ray/v2ray-core/proxy/internal" "github.com/v2ray/v2ray-core/transport/dialer" "github.com/v2ray/v2ray-core/transport/ray" ) type FreedomConnection struct { + domainStrategy DomainStrategy + dns dns.Server +} + +func NewFreedomConnection(config *Config, space app.Space) *FreedomConnection { + f := &FreedomConnection{ + domainStrategy: config.DomainStrategy, + } + log.Info("Freedom: Domain strategy: ", f.domainStrategy) + space.InitializeApplication(func() error { + if config.DomainStrategy == DomainStrategyUseIP { + if !space.HasApp(dns.APP_ID) { + log.Error("Freedom: DNS server is not found in the space.") + return app.ErrorMissingApplication + } + f.dns = space.GetApp(dns.APP_ID).(dns.Server) + } + return nil + }) + return f +} + +// @Private +func (this *FreedomConnection) ResolveIP(destination v2net.Destination) v2net.Destination { + if !destination.Address().IsDomain() { + return destination + } + + ips := this.dns.Get(destination.Address().Domain()) + if len(ips) == 0 { + log.Info("Freedom: DNS returns nil answer. Keep domain as is.") + return destination + } + + ip := ips[dice.Roll(len(ips))] + var newDest v2net.Destination + if destination.IsTCP() { + newDest = v2net.TCPDestination(v2net.IPAddress(ip), destination.Port()) + } else { + newDest = v2net.UDPDestination(v2net.IPAddress(ip), destination.Port()) + } + log.Info("Freedom: Changing destination from ", destination, " to ", newDest) + return newDest } func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload *alloc.Buffer, ray ray.OutboundRay) error { @@ -25,6 +73,9 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload * defer ray.OutboundOutput().Close() var conn net.Conn + if this.domainStrategy == DomainStrategyUseIP && destination.Address().IsDomain() { + destination = this.ResolveIP(destination) + } err := retry.Timed(5, 100).On(func() error { rawConn, err := dialer.Dial(destination) if err != nil { @@ -79,3 +130,10 @@ func (this *FreedomConnection) Dispatch(destination v2net.Destination, payload * return nil } + +func init() { + internal.MustRegisterOutboundHandlerCreator("freedom", + func(space app.Space, config interface{}) (proxy.OutboundHandler, error) { + return NewFreedomConnection(config.(*Config), space), nil + }) +} diff --git a/proxy/freedom/freedom_test.go b/proxy/freedom/freedom_test.go index a6fecee67..e9fa399eb 100644 --- a/proxy/freedom/freedom_test.go +++ b/proxy/freedom/freedom_test.go @@ -1,11 +1,20 @@ package freedom_test import ( + "net" "testing" + "github.com/v2ray/v2ray-core/app" + "github.com/v2ray/v2ray-core/app/dispatcher" + dispatchers "github.com/v2ray/v2ray-core/app/dispatcher/impl" + "github.com/v2ray/v2ray-core/app/dns" + "github.com/v2ray/v2ray-core/app/proxyman" + "github.com/v2ray/v2ray-core/app/router" + "github.com/v2ray/v2ray-core/app/router/rules" "github.com/v2ray/v2ray-core/common/alloc" v2net "github.com/v2ray/v2ray-core/common/net" v2nettesting "github.com/v2ray/v2ray-core/common/net/testing" + netassert "github.com/v2ray/v2ray-core/common/net/testing/assert" . "github.com/v2ray/v2ray-core/proxy/freedom" v2testing "github.com/v2ray/v2ray-core/testing" "github.com/v2ray/v2ray-core/testing/assert" @@ -29,7 +38,10 @@ func TestSinglePacket(t *testing.T) { _, err := tcpServer.Start() assert.Error(err).IsNil() - freedom := &FreedomConnection{} + space := app.NewSpace() + freedom := NewFreedomConnection(&Config{}, space) + space.Initialize() + traffic := ray.NewRay() data2Send := "Data to be sent to remote" payload := alloc.NewSmallBuffer().Clear().Append([]byte(data2Send)) @@ -47,7 +59,7 @@ func TestSinglePacket(t *testing.T) { func TestUnreachableDestination(t *testing.T) { v2testing.Current(t) - freedom := &FreedomConnection{} + freedom := NewFreedomConnection(&Config{}, app.NewSpace()) traffic := ray.NewRay() data2Send := "Data to be sent to remote" payload := alloc.NewSmallBuffer().Clear().Append([]byte(data2Send)) @@ -55,3 +67,27 @@ func TestUnreachableDestination(t *testing.T) { err := freedom.Dispatch(v2net.TCPDestination(v2net.IPAddress([]byte{127, 0, 0, 1}), 128), payload, traffic) assert.Error(err).IsNotNil() } + +func TestIPResolution(t *testing.T) { + v2testing.Current(t) + + space := app.NewSpace() + space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, proxyman.NewDefaultOutboundHandlerManager()) + space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) + r, _ := router.CreateRouter("rules", &rules.RouterRuleConfig{}, space) + space.BindApp(router.APP_ID, r) + dnsServer := dns.NewCacheServer(space, &dns.Config{ + Hosts: map[string]net.IP{ + "v2ray.com": net.IP([]byte{127, 0, 0, 1}), + }, + }) + space.BindApp(dns.APP_ID, dnsServer) + + freedom := NewFreedomConnection(&Config{DomainStrategy: DomainStrategyUseIP}, space) + + space.Initialize() + + ipDest := freedom.ResolveIP(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), v2net.Port(80))) + netassert.Destination(ipDest).IsTCP() + netassert.Address(ipDest.Address()).Equals(v2net.LocalHostIP) +} diff --git a/proxy/freedom/freedomfactory.go b/proxy/freedom/freedomfactory.go deleted file mode 100644 index a399eb64b..000000000 --- a/proxy/freedom/freedomfactory.go +++ /dev/null @@ -1,14 +0,0 @@ -package freedom - -import ( - "github.com/v2ray/v2ray-core/app" - "github.com/v2ray/v2ray-core/proxy" - "github.com/v2ray/v2ray-core/proxy/internal" -) - -func init() { - internal.MustRegisterOutboundHandlerCreator("freedom", - func(space app.Space, config interface{}) (proxy.OutboundHandler, error) { - return &FreedomConnection{}, nil - }) -}