forked from Psiphon-Labs/psiphon-tunnel-core
-
Notifications
You must be signed in to change notification settings - Fork 1
/
net.go
309 lines (275 loc) · 8.46 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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/*
* Copyright (c) 2016, Psiphon Inc.
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package common
import (
"container/list"
"context"
"net"
"net/http"
"strconv"
"sync"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
"github.com/miekg/dns"
"github.com/wader/filtertransport"
)
// Dialer is a custom network dialer.
type Dialer func(context.Context, string, string) (net.Conn, error)
// NetDialer mimicks the net.Dialer interface.
type NetDialer interface {
Dial(network, address string) (net.Conn, error)
DialContext(ctx context.Context, network, address string) (net.Conn, error)
}
// Closer defines the interface to a type, typically a net.Conn, that can be
// closed.
type Closer interface {
IsClosed() bool
}
// CloseWriter defines the interface to a type, typically a net.TCPConn, that
// implements CloseWrite.
type CloseWriter interface {
CloseWrite() error
}
// IrregularIndicator defines the interface for a type, typically a net.Conn,
// that detects and reports irregular conditions during initial network
// connection establishment.
type IrregularIndicator interface {
IrregularTunnelError() error
}
// UnderlyingTCPAddrSource defines the interface for a type, typically a
// net.Conn, such as a server meek Conn, which has an underlying TCP conn(s),
// providing access to the LocalAddr and RemoteAddr properties of the
// underlying TCP conn.
type UnderlyingTCPAddrSource interface {
// GetUnderlyingTCPAddrs returns the LocalAddr and RemoteAddr properties of
// the underlying TCP conn.
GetUnderlyingTCPAddrs() (*net.TCPAddr, *net.TCPAddr, bool)
}
// FragmentorReplayAccessor defines the interface for accessing replay properties
// of a fragmentor Conn.
type FragmentorReplayAccessor interface {
SetReplay(*prng.PRNG)
GetReplay() (*prng.Seed, bool)
}
// HTTPRoundTripper is an adapter that allows using a function as a
// http.RoundTripper.
type HTTPRoundTripper struct {
roundTrip func(*http.Request) (*http.Response, error)
}
// NewHTTPRoundTripper creates a new HTTPRoundTripper, using the specified
// roundTrip function for HTTP round trips.
func NewHTTPRoundTripper(
roundTrip func(*http.Request) (*http.Response, error)) *HTTPRoundTripper {
return &HTTPRoundTripper{roundTrip: roundTrip}
}
// RoundTrip implements http.RoundTripper RoundTrip.
func (h HTTPRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
return h.roundTrip(request)
}
// TerminateHTTPConnection sends a 404 response to a client and also closes
// the persistent connection.
func TerminateHTTPConnection(
responseWriter http.ResponseWriter, request *http.Request) {
http.NotFound(responseWriter, request)
hijack, ok := responseWriter.(http.Hijacker)
if !ok {
return
}
conn, buffer, err := hijack.Hijack()
if err != nil {
return
}
buffer.Flush()
conn.Close()
}
// IPAddressFromAddr is a helper which extracts an IP address
// from a net.Addr or returns "" if there is no IP address.
func IPAddressFromAddr(addr net.Addr) string {
ipAddress := ""
if addr != nil {
host, _, err := net.SplitHostPort(addr.String())
if err == nil {
ipAddress = host
}
}
return ipAddress
}
// PortFromAddr is a helper which extracts a port number from a net.Addr or
// returns 0 if there is no port number.
func PortFromAddr(addr net.Addr) int {
port := 0
if addr != nil {
_, portStr, err := net.SplitHostPort(addr.String())
if err == nil {
port, _ = strconv.Atoi(portStr)
}
}
return port
}
// Conns is a synchronized list of Conns that is used to coordinate
// interrupting a set of goroutines establishing connections, or
// close a set of open connections, etc.
// Once the list is closed, no more items may be added to the
// list (unless it is reset).
type Conns struct {
mutex sync.Mutex
isClosed bool
conns map[net.Conn]bool
}
// NewConns initializes a new Conns.
func NewConns() *Conns {
return &Conns{}
}
func (conns *Conns) Reset() {
conns.mutex.Lock()
defer conns.mutex.Unlock()
conns.isClosed = false
conns.conns = make(map[net.Conn]bool)
}
func (conns *Conns) Add(conn net.Conn) bool {
conns.mutex.Lock()
defer conns.mutex.Unlock()
if conns.isClosed {
return false
}
if conns.conns == nil {
conns.conns = make(map[net.Conn]bool)
}
conns.conns[conn] = true
return true
}
func (conns *Conns) Remove(conn net.Conn) {
conns.mutex.Lock()
defer conns.mutex.Unlock()
delete(conns.conns, conn)
}
func (conns *Conns) CloseAll() {
conns.mutex.Lock()
defer conns.mutex.Unlock()
conns.isClosed = true
for conn := range conns.conns {
conn.Close()
}
conns.conns = make(map[net.Conn]bool)
}
// LRUConns is a concurrency-safe list of net.Conns ordered
// by recent activity. Its purpose is to facilitate closing
// the oldest connection in a set of connections.
//
// New connections added are referenced by a LRUConnsEntry,
// which is used to Touch() active connections, which
// promotes them to the front of the order and to Remove()
// connections that are no longer LRU candidates.
//
// CloseOldest() will remove the oldest connection from the
// list and call net.Conn.Close() on the connection.
//
// After an entry has been removed, LRUConnsEntry Touch()
// and Remove() will have no effect.
type LRUConns struct {
mutex sync.Mutex
list *list.List
}
// NewLRUConns initializes a new LRUConns.
func NewLRUConns() *LRUConns {
return &LRUConns{list: list.New()}
}
// Add inserts a net.Conn as the freshest connection
// in a LRUConns and returns an LRUConnsEntry to be
// used to freshen the connection or remove the connection
// from the LRU list.
func (conns *LRUConns) Add(conn net.Conn) *LRUConnsEntry {
conns.mutex.Lock()
defer conns.mutex.Unlock()
return &LRUConnsEntry{
lruConns: conns,
element: conns.list.PushFront(conn),
}
}
// CloseOldest closes the oldest connection in a
// LRUConns. It calls net.Conn.Close() on the
// connection.
func (conns *LRUConns) CloseOldest() {
conns.mutex.Lock()
oldest := conns.list.Back()
if oldest != nil {
conns.list.Remove(oldest)
}
// Release mutex before closing conn
conns.mutex.Unlock()
if oldest != nil {
oldest.Value.(net.Conn).Close()
}
}
// LRUConnsEntry is an entry in a LRUConns list.
type LRUConnsEntry struct {
lruConns *LRUConns
element *list.Element
}
// Remove deletes the connection referenced by the
// LRUConnsEntry from the associated LRUConns.
// Has no effect if the entry was not initialized
// or previously removed.
func (entry *LRUConnsEntry) Remove() {
if entry.lruConns == nil || entry.element == nil {
return
}
entry.lruConns.mutex.Lock()
defer entry.lruConns.mutex.Unlock()
entry.lruConns.list.Remove(entry.element)
}
// Touch promotes the connection referenced by the
// LRUConnsEntry to the front of the associated LRUConns.
// Has no effect if the entry was not initialized
// or previously removed.
func (entry *LRUConnsEntry) Touch() {
if entry.lruConns == nil || entry.element == nil {
return
}
entry.lruConns.mutex.Lock()
defer entry.lruConns.mutex.Unlock()
entry.lruConns.list.MoveToFront(entry.element)
}
// IsBogon checks if the specified IP is a bogon (loopback, private addresses,
// link-local addresses, etc.)
func IsBogon(IP net.IP) bool {
return filtertransport.FindIPNet(
filtertransport.DefaultFilteredNetworks, IP)
}
// ParseDNSQuestion parses a DNS message. When the message is a query,
// the first question, a fully-qualified domain name, is returned.
//
// For other valid DNS messages, "" is returned. An error is returned only
// for invalid DNS messages.
//
// Limitations:
// - Only the first Question field is extracted.
// - ParseDNSQuestion only functions for plaintext DNS and cannot
// extract domains from DNS-over-TLS/HTTPS, etc.
func ParseDNSQuestion(request []byte) (string, error) {
m := new(dns.Msg)
err := m.Unpack(request)
if err != nil {
return "", errors.Trace(err)
}
if len(m.Question) > 0 {
return m.Question[0].Name, nil
}
return "", nil
}