-
Notifications
You must be signed in to change notification settings - Fork 0
/
broflake_impl.go
179 lines (143 loc) · 5.35 KB
/
broflake_impl.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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package chained
import (
"context"
"crypto/x509"
"math/rand"
"net"
"net/http"
"time"
"github.com/getlantern/broflake/clientcore"
broflake_common "github.com/getlantern/broflake/common"
"github.com/getlantern/common/config"
"github.com/getlantern/flashlight/v7/ops"
"github.com/getlantern/flashlight/v7/proxied"
"github.com/getlantern/golog"
)
func init() {
log = golog.LoggerFor("broflake")
broflake_common.SetDebugLogger(log.AsDebugLogger())
}
const (
// only wait 10 seconds before failing over to the next masquerade since signaling with Freddie only has a 25 second timeout
masqueradeTimeout = 10
)
type broflakeImpl struct {
reportDialCore reportDialCoreFn // TODO: I don't know what this is for yet
QUICLayer *clientcore.QUICLayer
ui *clientcore.UIImpl
}
func newBroflakeImpl(pc *config.ProxyConfig, reportDialCore reportDialCoreFn) (proxyImpl, error) {
// TODO: I don't know what the reportDialCoreFn is, and I'm not sure if I need to know. I'm
// just imitating the function signature and approach of other impls...
bo, wo, qo := makeBroflakeOptions(pc)
// Construct, init, and start a Broflake client!
bfconn, ui, err := clientcore.NewBroflake(bo, wo, nil)
if err != nil {
return nil, err
}
ql, err := clientcore.NewQUICLayer(bfconn, qo)
if err != nil {
return nil, err
}
ql.DialAndMaintainQUICConnection()
return &broflakeImpl{
reportDialCore: reportDialCore,
QUICLayer: ql,
ui: ui,
}, nil
}
func (b *broflakeImpl) dialServer(op *ops.Op, ctx context.Context) (net.Conn, error) {
// TODO: I don't know what to do with 'op'
return b.QUICLayer.DialContext(ctx)
}
func (b *broflakeImpl) close() {
b.QUICLayer.Close()
b.ui.Stop()
}
// makeBroflakeOptions constructs the options structs required by the Broflake client constructor,
// overriding fields with values supplied in a ProxyConfig as applicable
func makeBroflakeOptions(pc *config.ProxyConfig) (
*clientcore.BroflakeOptions,
*clientcore.WebRTCOptions,
*clientcore.QUICLayerOptions,
) {
// Override BroflakeOptions defaults as applicable
bo := clientcore.NewDefaultBroflakeOptions()
if cTableSize := ptSettingInt(pc, "broflake_ctablesize"); cTableSize != 0 {
bo.CTableSize = cTableSize
}
if pTableSize := ptSettingInt(pc, "broflake_ptablesize"); pTableSize != 0 {
bo.PTableSize = pTableSize
}
if busBufferSz := ptSettingInt(pc, "broflake_busbuffersz"); busBufferSz != 0 {
bo.BusBufferSz = busBufferSz
}
if netstated := ptSetting(pc, "broflake_netstated"); netstated != "" {
bo.Netstated = netstated
}
// Override WebRTCOptions defaults as applicable
wo := clientcore.NewDefaultWebRTCOptions()
if discoverySrv := ptSetting(pc, "broflake_discoverysrv"); discoverySrv != "" {
wo.DiscoverySrv = discoverySrv
}
if endpoint := ptSetting(pc, "broflake_endpoint"); endpoint != "" {
wo.Endpoint = endpoint
}
if genesisAddr := ptSetting(pc, "broflake_genesisaddr"); genesisAddr != "" {
wo.GenesisAddr = genesisAddr
}
// XXX: config.ProxyConfig pluggabletransportsettings do not support serialization of rich types like
// time.Duration. Consequently, we're somewhat riskily rehydrating our two timeout values here by
// assuming that the coefficient is time.Second. Beware!
if NATFailTimeout := ptSettingInt(pc, "broflake_natfailtimeout"); NATFailTimeout != 0 {
wo.NATFailTimeout = time.Duration(NATFailTimeout) * time.Second
}
if ICEFailTimeout := ptSettingInt(pc, "broflake_icefailtimeout"); ICEFailTimeout != 0 {
wo.ICEFailTimeout = time.Duration(ICEFailTimeout) * time.Second
}
if tag := ptSetting(pc, "broflake_tag"); tag != "" {
wo.Tag = tag
}
if STUNBatchSize := ptSettingInt(pc, "broflake_stunbatchsize"); STUNBatchSize != 0 {
wo.STUNBatchSize = uint32(STUNBatchSize)
}
// XXX: STUN servers are handled in a subtly different way than the rest of our settings overrides,
// because they're a nonscalar quantity passed via a different top level field in the ProxyConfig.
// If (and only if) a list of STUN servers has been supplied in the ProxyConfig, we'll override
// Broflake's default STUNBatch function.
if srvs := pc.GetStunServers(); srvs != nil {
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
wo.STUNBatch = func(size uint32) (batch []string, err error) {
return getRandomSubset(size, rng, srvs), nil
}
}
// Broflake's HTTP client isn't currently configurable via PluggableTransportSettings, and so
// we just give it this domain fronted client in all cases
wo.HttpClient = &http.Client{
Transport: proxied.Fronted(masqueradeTimeout),
}
// Override QUICLayerOptions defaults as applicable
qo := &clientcore.QUICLayerOptions{}
if serverName := ptSetting(pc, "broflake_egress_server_name"); serverName != "" {
qo.ServerName = serverName
}
qo.InsecureSkipVerify = ptSettingBool(pc, "broflake_egress_insecure_skip_verify")
if ca := ptSetting(pc, "broflake_egress_ca"); ca != "" {
qo.CA = x509.NewCertPool()
qo.CA.AppendCertsFromPEM([]byte(ca))
}
return bo, wo, qo
}
// getRandomSubset is a helper for our custom STUNBatch function. It returns a 'size'-sized
// random subset of the strings in 'set'.
func getRandomSubset(size uint32, rng *rand.Rand, set []string) []string {
if size > uint32(len(set)) {
size = uint32(len(set))
}
indices := rng.Perm(len(set))[:size]
subset := make([]string, 0, len(indices))
for _, i := range indices {
subset = append(subset, set[i])
}
return subset
}