-
Notifications
You must be signed in to change notification settings - Fork 0
/
freedom.go
147 lines (125 loc) · 3.81 KB
/
freedom.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package freedom
import (
"io"
"v2ray.com/core/app"
"v2ray.com/core/app/dns"
"v2ray.com/core/common/alloc"
"v2ray.com/core/common/dice"
v2io "v2ray.com/core/common/io"
"v2ray.com/core/common/loader"
"v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/retry"
"v2ray.com/core/proxy"
"v2ray.com/core/proxy/registry"
"v2ray.com/core/transport/internet"
"v2ray.com/core/transport/internet/tcp"
"v2ray.com/core/transport/ray"
)
type FreedomConnection struct {
domainStrategy Config_DomainStrategy
timeout uint32
dns dns.Server
meta *proxy.OutboundHandlerMeta
}
func NewFreedomConnection(config *Config, space app.Space, meta *proxy.OutboundHandlerMeta) *FreedomConnection {
f := &FreedomConnection{
domainStrategy: config.DomainStrategy,
timeout: config.Timeout,
meta: meta,
}
space.InitializeApplication(func() error {
if config.DomainStrategy == Config_USE_IP {
if !space.HasApp(dns.APP_ID) {
log.Error("Freedom: DNS server is not found in the space.")
return app.ErrMissingApplication
}
f.dns = space.GetApp(dns.APP_ID).(dns.Server)
}
return nil
})
return f
}
// Private: Visible for testing.
func (this *FreedomConnection) ResolveIP(destination v2net.Destination) v2net.Destination {
if !destination.Address.Family().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.Network == v2net.Network_TCP {
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 {
log.Info("Freedom: Opening connection to ", destination)
defer payload.Release()
defer ray.OutboundInput().Release()
defer ray.OutboundOutput().Close()
var conn internet.Connection
if this.domainStrategy == Config_USE_IP && destination.Address.Family().IsDomain() {
destination = this.ResolveIP(destination)
}
err := retry.Timed(5, 100).On(func() error {
rawConn, err := internet.Dial(this.meta.Address, destination, internet.DialerOptions{
Stream: this.meta.StreamSettings,
})
if err != nil {
return err
}
conn = rawConn
return nil
})
if err != nil {
log.Warning("Freedom: Failed to open connection to ", destination, ": ", err)
return err
}
defer conn.Close()
input := ray.OutboundInput()
output := ray.OutboundOutput()
if !payload.IsEmpty() {
conn.Write(payload.Value)
}
go func() {
v2writer := v2io.NewAdaptiveWriter(conn)
defer v2writer.Release()
v2io.Pipe(input, v2writer)
if tcpConn, ok := conn.(*tcp.RawConnection); ok {
tcpConn.CloseWrite()
}
}()
var reader io.Reader = conn
timeout := this.timeout
if destination.Network == v2net.Network_UDP {
timeout = 16
}
if timeout > 0 {
reader = v2net.NewTimeOutReader(timeout /* seconds */, conn)
}
v2reader := v2io.NewAdaptiveReader(reader)
v2io.Pipe(v2reader, output)
v2reader.Release()
ray.OutboundOutput().Close()
return nil
}
type FreedomFactory struct{}
func (this *FreedomFactory) StreamCapability() v2net.NetworkList {
return v2net.NetworkList{
Network: []v2net.Network{v2net.Network_RawTCP},
}
}
func (this *FreedomFactory) Create(space app.Space, config interface{}, meta *proxy.OutboundHandlerMeta) (proxy.OutboundHandler, error) {
return NewFreedomConnection(config.(*Config), space, meta), nil
}
func init() {
registry.MustRegisterOutboundHandlerCreator(loader.GetType(new(Config)), new(FreedomFactory))
}