forked from viamrobotics/goutils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
net.go
167 lines (148 loc) · 4.93 KB
/
net.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
package utils
import (
"crypto/tls"
"fmt"
"net"
"github.com/pkg/errors"
"go.uber.org/multierr"
)
// TryReserveRandomPort attempts to "reserve" a random port for later use.
// It works by listening on a TCP port and immediately closing that listener.
// In most contexts this is reliable if the port is immediately used after and
// there is not much port churn. Typically an OS will monotonically increase the
// port numbers it assigns.
func TryReserveRandomPort() (port int, err error) {
//nolint:gosec
listener, err := net.Listen("tcp", ":0")
if err != nil {
return 0, err
}
defer func() {
err = multierr.Combine(err, listener.Close())
}()
return listener.Addr().(*net.TCPAddr).Port, nil
}
// GetAllLocalIPv4s finds all the local ips from all interfaces
// It only returns IPv4 addresses, and tries not to return any loopback addresses.
func GetAllLocalIPv4s() ([]string, error) {
allInterfaces, err := net.Interfaces()
if err != nil {
return nil, err
}
all := []string{}
for _, i := range allInterfaces {
addrs, err := i.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
switch v := addr.(type) {
case *net.IPNet:
if v.IP.IsLoopback() {
continue
}
ones, bits := v.Mask.Size()
if bits != 32 {
// this is what limits to ipv4
continue
}
if ones == bits {
// this means it's a loopback of some sort
// likely a bridge to parallels or docker or something
continue
}
all = append(all, v.IP.String())
default:
return nil, fmt.Errorf("unknow address type: %T", v)
}
}
}
return all, nil
}
// ErrInsufficientX509KeyPair is returned when an incomplete X509 key pair is used.
var ErrInsufficientX509KeyPair = errors.New("must provide both cert and key of an X509 key pair, not just one part")
const defaultListenAddress = "localhost:"
// NewPossiblySecureTCPListenerFromFile returns a TCP listener at the given address that is
// either insecure or TLS based listener depending on presence of the tlsCertFile and tlsKeyFile
// which are expected to be an X509 key pair. If no address is specified, the listener will bind
// to localhost IPV4 on a random port.
func NewPossiblySecureTCPListenerFromFile(address, tlsCertFile, tlsKeyFile string) (net.Listener, bool, error) {
if (tlsCertFile == "") != (tlsKeyFile == "") {
return nil, false, ErrInsufficientX509KeyPair
}
if address == "" {
address = defaultListenAddress
}
if tlsCertFile == "" || tlsKeyFile == "" {
insecureListener, err := net.Listen("tcp", address)
if err != nil {
return nil, false, err
}
return insecureListener, false, nil
}
cert, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
if err != nil {
return nil, false, err
}
return newTLSListener(address, &tls.Config{
MinVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{cert},
})
}
// NewPossiblySecureTCPListenerFromMemory returns a TCP listener at the given address that is
// either insecure or TLS based listener depending on presence of the tlsCertPEM and tlsKeyPEM
// which are expected to be an X509 key pair. If no address is specified, the listener will bind
// to localhost IPV4 on a random port.
func NewPossiblySecureTCPListenerFromMemory(address string, tlsCertPEM, tlsKeyPEM []byte) (net.Listener, bool, error) {
if (len(tlsCertPEM) == 0) != (len(tlsKeyPEM) == 0) {
return nil, false, ErrInsufficientX509KeyPair
}
if address == "" {
address = defaultListenAddress
}
if len(tlsCertPEM) == 0 || len(tlsKeyPEM) == 0 {
insecureListener, err := net.Listen("tcp", address)
if err != nil {
return nil, false, err
}
return insecureListener, false, nil
}
cert, err := tls.X509KeyPair(tlsCertPEM, tlsKeyPEM)
if err != nil {
return nil, false, err
}
return newTLSListener(address, &tls.Config{
MinVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{cert},
})
}
// NewPossiblySecureTCPListenerFromConfig returns a TCP listener at the given address that is
// either insecure or TLS based listener depending on presence of certificates in the given
// TLS Config. If no address is specified, the listener will bind to localhost IPV4 on a random port.
func NewPossiblySecureTCPListenerFromConfig(address string, tlsConfig *tls.Config) (net.Listener, bool, error) {
if address == "" {
address = defaultListenAddress
}
if len(tlsConfig.Certificates) == 0 {
// try getting it a different way
if _, err := tlsConfig.GetCertificate(&tls.ClientHelloInfo{}); err != nil {
insecureListener, err := net.Listen("tcp", address)
if err != nil {
return nil, false, err
}
return insecureListener, false, nil
}
}
return newTLSListener(address, tlsConfig)
}
func newTLSListener(address string, config *tls.Config) (net.Listener, bool, error) {
cloned := config.Clone()
if cloned.MinVersion == 0 {
cloned.MinVersion = tls.VersionTLS12
}
secureListener, err := tls.Listen("tcp", address, cloned)
if err != nil {
return nil, false, err
}
return secureListener, true, nil
}