-
-
Notifications
You must be signed in to change notification settings - Fork 13.3k
/
port.go
116 lines (99 loc) · 2.07 KB
/
port.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
package port
import (
"fmt"
"net"
"strconv"
"sync"
"k8s.io/apimachinery/pkg/util/sets"
)
type Allocator struct {
reserved sets.Set[int]
used sets.Set[int]
mu sync.Mutex
}
// NewAllocator return a port allocator for testing.
// Example: from: 10, to: 20, mod 4, index 1
// Reserved ports: 13, 17
func NewAllocator(from int, to int, mod int, index int) *Allocator {
pa := &Allocator{
reserved: sets.New[int](),
used: sets.New[int](),
}
for i := from; i <= to; i++ {
if i%mod == index {
pa.reserved.Insert(i)
}
}
return pa
}
func (pa *Allocator) Get() int {
return pa.GetByName("")
}
func (pa *Allocator) GetByName(portName string) int {
var builder *nameBuilder
if portName == "" {
builder = &nameBuilder{}
} else {
var err error
builder, err = unmarshalFromName(portName)
if err != nil {
fmt.Println(err, portName)
return 0
}
}
pa.mu.Lock()
defer pa.mu.Unlock()
for i := 0; i < 20; i++ {
port := pa.getByRange(builder.rangePortFrom, builder.rangePortTo)
if port == 0 {
return 0
}
l, err := net.Listen("tcp", net.JoinHostPort("0.0.0.0", strconv.Itoa(port)))
if err != nil {
// Maybe not controlled by us, mark it used.
pa.used.Insert(port)
continue
}
l.Close()
udpAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort("0.0.0.0", strconv.Itoa(port)))
if err != nil {
continue
}
udpConn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
// Maybe not controlled by us, mark it used.
pa.used.Insert(port)
continue
}
udpConn.Close()
pa.used.Insert(port)
pa.reserved.Delete(port)
return port
}
return 0
}
func (pa *Allocator) getByRange(from, to int) int {
if from <= 0 {
port, _ := pa.reserved.PopAny()
return port
}
// choose a random port between from - to
ports := pa.reserved.UnsortedList()
for _, port := range ports {
if port >= from && port <= to {
return port
}
}
return 0
}
func (pa *Allocator) Release(port int) {
if port <= 0 {
return
}
pa.mu.Lock()
defer pa.mu.Unlock()
if pa.used.Has(port) {
pa.used.Delete(port)
pa.reserved.Insert(port)
}
}