cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2018.9.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

origin/supervisor.go

234lines · modeblame

d06fc520Areg Harutyunyan8 years ago1package origin
2
3import (
4"fmt"
5"net"
6"time"
7
8"golang.org/x/net/context"
9)
10
11const (
12// Waiting time before retrying a failed tunnel connection
13tunnelRetryDuration = time.Second * 10
14// SRV record resolution TTL
15resolveTTL = time.Hour
16// Interval between registering new tunnels
17registrationInterval = time.Second
18)
19
20type Supervisor struct {
21config *TunnelConfig
22edgeIPs []*net.TCPAddr
23// nextUnusedEdgeIP is the index of the next addr k edgeIPs to try
24nextUnusedEdgeIP int
25lastResolve time.Time
26resolverC chan resolveResult
27tunnelErrors chan tunnelError
28tunnelsConnecting map[int]chan struct{}
29// nextConnectedIndex and nextConnectedSignal are used to wait for all
30// currently-connecting tunnels to finish connecting so we can reset backoff timer
31nextConnectedIndex int
32nextConnectedSignal chan struct{}
33}
34
35type resolveResult struct {
36edgeIPs []*net.TCPAddr
37err error
38}
39
40type tunnelError struct {
41index int
42err error
43}
44
45func NewSupervisor(config *TunnelConfig) *Supervisor {
46return &Supervisor{
47config: config,
48tunnelErrors: make(chan tunnelError),
49tunnelsConnecting: map[int]chan struct{}{},
50}
51}
52
53func (s *Supervisor) Run(ctx context.Context, connectedSignal chan struct{}) error {
54logger := s.config.Logger
55if err := s.initialize(ctx, connectedSignal); err != nil {
56return err
57}
58var tunnelsWaiting []int
59backoff := BackoffHandler{MaxRetries: s.config.Retries, BaseTime: tunnelRetryDuration, RetryForever: true}
60var backoffTimer <-chan time.Time
61tunnelsActive := s.config.HAConnections
62
63for {
64select {
65// Context cancelled
66case <-ctx.Done():
67for tunnelsActive > 0 {
68<-s.tunnelErrors
69tunnelsActive--
70}
71return nil
72// startTunnel returned with error
73// (note that this may also be caused by context cancellation)
74case tunnelError := <-s.tunnelErrors:
75tunnelsActive--
76if tunnelError.err != nil {
77logger.WithError(tunnelError.err).Warn("Tunnel disconnected due to error")
78tunnelsWaiting = append(tunnelsWaiting, tunnelError.index)
79s.waitForNextTunnel(tunnelError.index)
80
81if backoffTimer == nil {
82backoffTimer = backoff.BackoffTimer()
83}
84
85// If the error is a dial error, the problem is likely to be network related
86// try another addr before refreshing since we are likely to get back the
87// same IPs in the same order. Same problem with duplicate connection error.
88if s.unusedIPs() {
89s.replaceEdgeIP(tunnelError.index)
90} else {
91s.refreshEdgeIPs()
92}
93}
94// Backoff was set and its timer expired
95case <-backoffTimer:
96backoffTimer = nil
97for _, index := range tunnelsWaiting {
98go s.startTunnel(ctx, index, s.newConnectedTunnelSignal(index))
99}
100tunnelsActive += len(tunnelsWaiting)
101tunnelsWaiting = nil
102// Tunnel successfully connected
103case <-s.nextConnectedSignal:
104if !s.waitForNextTunnel(s.nextConnectedIndex) && len(tunnelsWaiting) == 0 {
105// No more tunnels outstanding, clear backoff timer
106backoff.SetGracePeriod()
107}
108// DNS resolution returned
109case result := <-s.resolverC:
110s.lastResolve = time.Now()
111s.resolverC = nil
112if result.err == nil {
113logger.Debug("Service discovery refresh complete")
114s.edgeIPs = result.edgeIPs
115} else {
116logger.WithError(result.err).Error("Service discovery error")
117}
118}
119}
120}
121
122func (s *Supervisor) initialize(ctx context.Context, connectedSignal chan struct{}) error {
123logger := s.config.Logger
124edgeIPs, err := ResolveEdgeIPs(s.config.EdgeAddrs)
125if err != nil {
126logger.Infof("ResolveEdgeIPs err")
127return err
128}
129s.edgeIPs = edgeIPs
130if s.config.HAConnections > len(edgeIPs) {
131logger.Warnf("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, len(edgeIPs))
132s.config.HAConnections = len(edgeIPs)
133}
134s.lastResolve = time.Now()
135// check entitlement and version too old error before attempting to register more tunnels
136s.nextUnusedEdgeIP = s.config.HAConnections
137go s.startFirstTunnel(ctx, connectedSignal)
138select {
139case <-ctx.Done():
140<-s.tunnelErrors
141// Error can't be nil. A nil error signals that initialization succeed
142return fmt.Errorf("context was canceled")
143case tunnelError := <-s.tunnelErrors:
144return tunnelError.err
145case <-connectedSignal:
146}
147// At least one successful connection, so start the rest
148for i := 1; i < s.config.HAConnections; i++ {
149go s.startTunnel(ctx, i, make(chan struct{}))
150time.Sleep(registrationInterval)
151}
152return nil
153}
154
155// startTunnel starts the first tunnel connection. The resulting error will be sent on
156// s.tunnelErrors. It will send a signal via connectedSignal if registration succeed
157func (s *Supervisor) startFirstTunnel(ctx context.Context, connectedSignal chan struct{}) {
158err := ServeTunnelLoop(ctx, s.config, s.getEdgeIP(0), 0, connectedSignal)
159defer func() {
160s.tunnelErrors <- tunnelError{index: 0, err: err}
161}()
162
163for s.unusedIPs() {
164select {
165case <-ctx.Done():
166return
167default:
168}
169switch err.(type) {
170case nil:
171return
172// try the next address if it was a dialError(network problem) or
173// dupConnRegisterTunnelError
174case dialError, dupConnRegisterTunnelError:
175s.replaceEdgeIP(0)
176default:
177return
178}
179err = ServeTunnelLoop(ctx, s.config, s.getEdgeIP(0), 0, connectedSignal)
180}
181}
182
183// startTunnel starts a new tunnel connection. The resulting error will be sent on
184// s.tunnelErrors.
185func (s *Supervisor) startTunnel(ctx context.Context, index int, connectedSignal chan struct{}) {
186err := ServeTunnelLoop(ctx, s.config, s.getEdgeIP(index), uint8(index), connectedSignal)
187s.tunnelErrors <- tunnelError{index: index, err: err}
188}
189
190func (s *Supervisor) newConnectedTunnelSignal(index int) chan struct{} {
191signal := make(chan struct{})
192s.tunnelsConnecting[index] = signal
193s.nextConnectedSignal = signal
194s.nextConnectedIndex = index
195return signal
196}
197
198func (s *Supervisor) waitForNextTunnel(index int) bool {
199delete(s.tunnelsConnecting, index)
200s.nextConnectedSignal = nil
201for k, v := range s.tunnelsConnecting {
202s.nextConnectedIndex = k
203s.nextConnectedSignal = v
204return true
205}
206return false
207}
208
209func (s *Supervisor) getEdgeIP(index int) *net.TCPAddr {
210return s.edgeIPs[index%len(s.edgeIPs)]
211}
212
213func (s *Supervisor) refreshEdgeIPs() {
214if s.resolverC != nil {
215return
216}
217if time.Since(s.lastResolve) < resolveTTL {
218return
219}
220s.resolverC = make(chan resolveResult)
221go func() {
222edgeIPs, err := ResolveEdgeIPs(s.config.EdgeAddrs)
223s.resolverC <- resolveResult{edgeIPs: edgeIPs, err: err}
224}()
225}
226
227func (s *Supervisor) unusedIPs() bool {
228return s.nextUnusedEdgeIP < len(s.edgeIPs)
229}
230
231func (s *Supervisor) replaceEdgeIP(badIPIndex int) {
232s.edgeIPs[badIPIndex] = s.edgeIPs[s.nextUnusedEdgeIP]
233s.nextUnusedEdgeIP++
234}