/
dialer.go
155 lines (133 loc) · 4.86 KB
/
dialer.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
148
149
150
151
152
153
154
155
package tapdance
import (
"context"
"errors"
"net"
"github.com/refraction-networking/conjure/application/transports/wrapping/min"
"github.com/refraction-networking/conjure/application/transports/wrapping/obfs4"
pb "github.com/refraction-networking/gotapdance/protobuf"
)
var sessionsTotal CounterUint64
var randomizePortDefault = false
// Dialer contains options and implements advanced functions for establishing TapDance connection.
type Dialer struct {
SplitFlows bool
// THIS IS REQUIRED TO INTERFACE WITH PSIPHON ANDROID
// we use their dialer to prevent connection loopback into our own proxy
// connection when tunneling the whole device.
Dialer func(context.Context, string, string) (net.Conn, error)
DarkDecoy bool
// The type of registrar to use when performing Conjure registrations.
DarkDecoyRegistrar Registrar
// The type of transport to use for Conjure connections.
Transport pb.TransportType
TransportConfig Transport
UseProxyHeader bool
V6Support bool
Width int
// Subnet that we want to limit to (or empty if they're all fine)
PhantomNet string
}
// Dial connects to the address on the named network.
//
// The only supported network at this time: "tcp".
// The address has the form "host:port".
// The host must be a literal IP address, or a host name that can be
// resolved to IP addresses.
// To avoid abuse, only certain whitelisted ports are allowed.
//
// Example: Dial("tcp", "golang.org:80")
func Dial(network, address string) (net.Conn, error) {
var d Dialer
return d.Dial(network, address)
}
// Dial connects to the address on the named network.
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
return d.DialContext(context.Background(), network, address)
}
// DialContext connects to the address on the named network using the provided context.
// Long deadline is strongly advised, since tapdance will try multiple decoys.
//
// The only supported network at this time: "tcp".
// The address has the form "host:port".
// The host must be a literal IP address, or a host name that can be
// resolved to IP addresses.
// To avoid abuse, only certain whitelisted ports are allowed.
//
// Example: Dial("tcp", "golang.org:80")
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
if network != "tcp" {
return nil, &net.OpError{Op: "dial", Net: network, Err: net.UnknownNetworkError(network)}
}
if len(address) > 0 {
_, _, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
}
if d.Dialer == nil {
// custom dialer is not set, use default
defaultDialer := net.Dialer{}
d.Dialer = defaultDialer.DialContext
}
if !d.SplitFlows {
if !d.DarkDecoy {
flow, err := makeTdFlow(flowBidirectional, nil, address)
if err != nil {
return nil, err
}
flow.tdRaw.Dialer = d.Dialer
flow.tdRaw.useProxyHeader = d.UseProxyHeader
return flow, flow.DialContext(ctx)
}
// Conjure
var cjSession *ConjureSession
transport := d.TransportConfig
if d.TransportConfig == nil {
switch d.Transport {
case pb.TransportType_Min:
transport = &min.ClientTransport{Parameters: &pb.GenericTransportParams{RandomizeDstPort: &randomizePortDefault}}
case pb.TransportType_Obfs4:
transport = &obfs4.ClientTransport{Parameters: &pb.GenericTransportParams{RandomizeDstPort: &randomizePortDefault}}
default:
return nil, errors.New("unknown transport by TransportType try using TransportConfig")
}
}
// If specified, only select a phantom from a given range
if d.PhantomNet != "" {
_, phantomRange, err := net.ParseCIDR(d.PhantomNet)
if err != nil {
return nil, errors.New("Invalid Phantom network goal")
}
cjSession = FindConjureSessionInRange(address, d.TransportConfig, phantomRange)
if cjSession == nil {
return nil, errors.New("Failed to find Phantom in target subnet")
}
} else {
cjSession = MakeConjureSession(address, transport)
}
cjSession.Dialer = d.Dialer
cjSession.UseProxyHeader = d.UseProxyHeader
cjSession.Width = uint(d.Width)
if d.V6Support {
cjSession.V6Support = &V6{include: both, support: true}
} else {
cjSession.V6Support = &V6{include: v4, support: false}
}
if len(address) == 0 {
return nil, errors.New("Dark Decoys require target address to be set")
}
return DialConjure(ctx, cjSession, d.DarkDecoyRegistrar)
}
return nil, errors.New("SplitFlows are not supported")
}
// DialProxy establishes direct connection to TapDance station proxy.
// Users are expected to send HTTP CONNECT request next.
func (d *Dialer) DialProxy() (net.Conn, error) {
return d.DialProxyContext(context.Background())
}
// DialProxyContext establishes direct connection to TapDance station proxy using the provided context.
// Users are expected to send HTTP CONNECT request next.
func (d *Dialer) DialProxyContext(ctx context.Context) (net.Conn, error) {
return d.DialContext(ctx, "tcp", "")
}