cloudflare/cloudflared

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2020.6.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

edgediscovery/allregions/regions.go

125lines · modecode

1package allregions
2
3import (
4 "fmt"
5 "net"
6
7 "github.com/cloudflare/cloudflared/logger"
8)
9
10// Regions stores Cloudflare edge network IPs, partitioned into two regions.
11// This is NOT thread-safe. Users of this package should use it with a lock.
12type Regions struct {
13 region1 Region
14 region2 Region
15}
16
17// ------------------------------------
18// Constructors
19// ------------------------------------
20
21// ResolveEdge resolves the Cloudflare edge, returning all regions discovered.
22func ResolveEdge(logger logger.Service) (*Regions, error) {
23 addrLists, err := edgeDiscovery(logger)
24 if err != nil {
25 return nil, err
26 }
27 if len(addrLists) < 2 {
28 return nil, fmt.Errorf("expected at least 2 Cloudflare Regions regions, but SRV only returned %v", len(addrLists))
29 }
30 return &Regions{
31 region1: NewRegion(addrLists[0]),
32 region2: NewRegion(addrLists[1]),
33 }, nil
34}
35
36// StaticEdge creates a list of edge addresses from the list of hostnames.
37// Mainly used for testing connectivity.
38func StaticEdge(hostnames []string) (*Regions, error) {
39 addrs, err := ResolveAddrs(hostnames)
40 if err != nil {
41 return nil, err
42 }
43 return NewNoResolve(addrs), nil
44}
45
46// NewNoResolve doesn't resolve the edge. Instead it just uses the given addresses.
47// You probably only need this for testing.
48func NewNoResolve(addrs []*net.TCPAddr) *Regions {
49 region1 := make([]*net.TCPAddr, 0)
50 region2 := make([]*net.TCPAddr, 0)
51 for i, v := range addrs {
52 if i%2 == 0 {
53 region1 = append(region1, v)
54 } else {
55 region2 = append(region2, v)
56 }
57 }
58
59 return &Regions{
60 region1: NewRegion(region1),
61 region2: NewRegion(region2),
62 }
63}
64
65// ------------------------------------
66// Methods
67// ------------------------------------
68
69// GetAnyAddress returns an arbitrary address from the larger region.
70func (rs *Regions) GetAnyAddress() *net.TCPAddr {
71 if addr := rs.region1.GetAnyAddress(); addr != nil {
72 return addr
73 }
74 return rs.region2.GetAnyAddress()
75}
76
77// AddrUsedBy finds the address used by the given connection.
78// Returns nil if the connection isn't using an address.
79func (rs *Regions) AddrUsedBy(connID int) *net.TCPAddr {
80 if addr := rs.region1.AddrUsedBy(connID); addr != nil {
81 return addr
82 }
83 return rs.region2.AddrUsedBy(connID)
84}
85
86// GetUnusedAddr gets an unused addr from the edge, excluding the given addr. Prefer to use addresses
87// evenly across both regions.
88func (rs *Regions) GetUnusedAddr(excluding *net.TCPAddr, connID int) *net.TCPAddr {
89 if rs.region1.AvailableAddrs() > rs.region2.AvailableAddrs() {
90 return getAddrs(excluding, connID, &rs.region1, &rs.region2)
91 }
92
93 return getAddrs(excluding, connID, &rs.region2, &rs.region1)
94}
95
96// getAddrs tries to grab address form `first` region, then `second` region
97// this is an unrolled loop over 2 element array
98func getAddrs(excluding *net.TCPAddr, connID int, first *Region, second *Region) *net.TCPAddr {
99 addr := first.GetUnusedIP(excluding)
100 if addr != nil {
101 first.Use(addr, connID)
102 return addr
103 }
104 addr = second.GetUnusedIP(excluding)
105 if addr != nil {
106 second.Use(addr, connID)
107 return addr
108 }
109
110 return nil
111}
112
113// AvailableAddrs returns how many edge addresses aren't used.
114func (rs *Regions) AvailableAddrs() int {
115 return rs.region1.AvailableAddrs() + rs.region2.AvailableAddrs()
116}
117
118// GiveBack the address so that other connections can use it.
119// Returns true if the address is in this edge.
120func (rs *Regions) GiveBack(addr *net.TCPAddr) bool {
121 if found := rs.region1.GiveBack(addr); found {
122 return found
123 }
124 return rs.region2.GiveBack(addr)
125}
126