github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/net.go (about) 1 /* 2 * Copyright (c) 2016, Psiphon Inc. 3 * All rights reserved. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package common 21 22 import ( 23 "container/list" 24 "context" 25 "net" 26 "net/http" 27 "strconv" 28 "sync" 29 30 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors" 31 "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng" 32 "github.com/miekg/dns" 33 "github.com/wader/filtertransport" 34 ) 35 36 // Dialer is a custom network dialer. 37 type Dialer func(context.Context, string, string) (net.Conn, error) 38 39 // NetDialer mimicks the net.Dialer interface. 40 type NetDialer interface { 41 Dial(network, address string) (net.Conn, error) 42 DialContext(ctx context.Context, network, address string) (net.Conn, error) 43 } 44 45 // Closer defines the interface to a type, typically a net.Conn, that can be 46 // closed. 47 type Closer interface { 48 IsClosed() bool 49 } 50 51 // CloseWriter defines the interface to a type, typically a net.TCPConn, that 52 // implements CloseWrite. 53 type CloseWriter interface { 54 CloseWrite() error 55 } 56 57 // IrregularIndicator defines the interface for a type, typically a net.Conn, 58 // that detects and reports irregular conditions during initial network 59 // connection establishment. 60 type IrregularIndicator interface { 61 IrregularTunnelError() error 62 } 63 64 // UnderlyingTCPAddrSource defines the interface for a type, typically a 65 // net.Conn, such as a server meek Conn, which has an underlying TCP conn(s), 66 // providing access to the LocalAddr and RemoteAddr properties of the 67 // underlying TCP conn. 68 type UnderlyingTCPAddrSource interface { 69 70 // GetUnderlyingTCPAddrs returns the LocalAddr and RemoteAddr properties of 71 // the underlying TCP conn. 72 GetUnderlyingTCPAddrs() (*net.TCPAddr, *net.TCPAddr, bool) 73 } 74 75 // FragmentorReplayAccessor defines the interface for accessing replay properties 76 // of a fragmentor Conn. 77 type FragmentorReplayAccessor interface { 78 SetReplay(*prng.PRNG) 79 GetReplay() (*prng.Seed, bool) 80 } 81 82 // HTTPRoundTripper is an adapter that allows using a function as a 83 // http.RoundTripper. 84 type HTTPRoundTripper struct { 85 roundTrip func(*http.Request) (*http.Response, error) 86 } 87 88 // NewHTTPRoundTripper creates a new HTTPRoundTripper, using the specified 89 // roundTrip function for HTTP round trips. 90 func NewHTTPRoundTripper( 91 roundTrip func(*http.Request) (*http.Response, error)) *HTTPRoundTripper { 92 return &HTTPRoundTripper{roundTrip: roundTrip} 93 } 94 95 // RoundTrip implements http.RoundTripper RoundTrip. 96 func (h HTTPRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) { 97 return h.roundTrip(request) 98 } 99 100 // TerminateHTTPConnection sends a 404 response to a client and also closes 101 // the persistent connection. 102 func TerminateHTTPConnection( 103 responseWriter http.ResponseWriter, request *http.Request) { 104 105 http.NotFound(responseWriter, request) 106 107 hijack, ok := responseWriter.(http.Hijacker) 108 if !ok { 109 return 110 } 111 conn, buffer, err := hijack.Hijack() 112 if err != nil { 113 return 114 } 115 buffer.Flush() 116 conn.Close() 117 } 118 119 // IPAddressFromAddr is a helper which extracts an IP address 120 // from a net.Addr or returns "" if there is no IP address. 121 func IPAddressFromAddr(addr net.Addr) string { 122 ipAddress := "" 123 if addr != nil { 124 host, _, err := net.SplitHostPort(addr.String()) 125 if err == nil { 126 ipAddress = host 127 } 128 } 129 return ipAddress 130 } 131 132 // PortFromAddr is a helper which extracts a port number from a net.Addr or 133 // returns 0 if there is no port number. 134 func PortFromAddr(addr net.Addr) int { 135 port := 0 136 if addr != nil { 137 _, portStr, err := net.SplitHostPort(addr.String()) 138 if err == nil { 139 port, _ = strconv.Atoi(portStr) 140 } 141 } 142 return port 143 } 144 145 // Conns is a synchronized list of Conns that is used to coordinate 146 // interrupting a set of goroutines establishing connections, or 147 // close a set of open connections, etc. 148 // Once the list is closed, no more items may be added to the 149 // list (unless it is reset). 150 type Conns struct { 151 mutex sync.Mutex 152 isClosed bool 153 conns map[net.Conn]bool 154 } 155 156 // NewConns initializes a new Conns. 157 func NewConns() *Conns { 158 return &Conns{} 159 } 160 161 func (conns *Conns) Reset() { 162 conns.mutex.Lock() 163 defer conns.mutex.Unlock() 164 conns.isClosed = false 165 conns.conns = make(map[net.Conn]bool) 166 } 167 168 func (conns *Conns) Add(conn net.Conn) bool { 169 conns.mutex.Lock() 170 defer conns.mutex.Unlock() 171 if conns.isClosed { 172 return false 173 } 174 if conns.conns == nil { 175 conns.conns = make(map[net.Conn]bool) 176 } 177 conns.conns[conn] = true 178 return true 179 } 180 181 func (conns *Conns) Remove(conn net.Conn) { 182 conns.mutex.Lock() 183 defer conns.mutex.Unlock() 184 delete(conns.conns, conn) 185 } 186 187 func (conns *Conns) CloseAll() { 188 conns.mutex.Lock() 189 defer conns.mutex.Unlock() 190 conns.isClosed = true 191 for conn := range conns.conns { 192 conn.Close() 193 } 194 conns.conns = make(map[net.Conn]bool) 195 } 196 197 // LRUConns is a concurrency-safe list of net.Conns ordered 198 // by recent activity. Its purpose is to facilitate closing 199 // the oldest connection in a set of connections. 200 // 201 // New connections added are referenced by a LRUConnsEntry, 202 // which is used to Touch() active connections, which 203 // promotes them to the front of the order and to Remove() 204 // connections that are no longer LRU candidates. 205 // 206 // CloseOldest() will remove the oldest connection from the 207 // list and call net.Conn.Close() on the connection. 208 // 209 // After an entry has been removed, LRUConnsEntry Touch() 210 // and Remove() will have no effect. 211 type LRUConns struct { 212 mutex sync.Mutex 213 list *list.List 214 } 215 216 // NewLRUConns initializes a new LRUConns. 217 func NewLRUConns() *LRUConns { 218 return &LRUConns{list: list.New()} 219 } 220 221 // Add inserts a net.Conn as the freshest connection 222 // in a LRUConns and returns an LRUConnsEntry to be 223 // used to freshen the connection or remove the connection 224 // from the LRU list. 225 func (conns *LRUConns) Add(conn net.Conn) *LRUConnsEntry { 226 conns.mutex.Lock() 227 defer conns.mutex.Unlock() 228 return &LRUConnsEntry{ 229 lruConns: conns, 230 element: conns.list.PushFront(conn), 231 } 232 } 233 234 // CloseOldest closes the oldest connection in a 235 // LRUConns. It calls net.Conn.Close() on the 236 // connection. 237 func (conns *LRUConns) CloseOldest() { 238 conns.mutex.Lock() 239 oldest := conns.list.Back() 240 if oldest != nil { 241 conns.list.Remove(oldest) 242 } 243 // Release mutex before closing conn 244 conns.mutex.Unlock() 245 if oldest != nil { 246 oldest.Value.(net.Conn).Close() 247 } 248 } 249 250 // LRUConnsEntry is an entry in a LRUConns list. 251 type LRUConnsEntry struct { 252 lruConns *LRUConns 253 element *list.Element 254 } 255 256 // Remove deletes the connection referenced by the 257 // LRUConnsEntry from the associated LRUConns. 258 // Has no effect if the entry was not initialized 259 // or previously removed. 260 func (entry *LRUConnsEntry) Remove() { 261 if entry.lruConns == nil || entry.element == nil { 262 return 263 } 264 entry.lruConns.mutex.Lock() 265 defer entry.lruConns.mutex.Unlock() 266 entry.lruConns.list.Remove(entry.element) 267 } 268 269 // Touch promotes the connection referenced by the 270 // LRUConnsEntry to the front of the associated LRUConns. 271 // Has no effect if the entry was not initialized 272 // or previously removed. 273 func (entry *LRUConnsEntry) Touch() { 274 if entry.lruConns == nil || entry.element == nil { 275 return 276 } 277 entry.lruConns.mutex.Lock() 278 defer entry.lruConns.mutex.Unlock() 279 entry.lruConns.list.MoveToFront(entry.element) 280 } 281 282 // IsBogon checks if the specified IP is a bogon (loopback, private addresses, 283 // link-local addresses, etc.) 284 func IsBogon(IP net.IP) bool { 285 return filtertransport.FindIPNet( 286 filtertransport.DefaultFilteredNetworks, IP) 287 } 288 289 // ParseDNSQuestion parses a DNS message. When the message is a query, 290 // the first question, a fully-qualified domain name, is returned. 291 // 292 // For other valid DNS messages, "" is returned. An error is returned only 293 // for invalid DNS messages. 294 // 295 // Limitations: 296 // - Only the first Question field is extracted. 297 // - ParseDNSQuestion only functions for plaintext DNS and cannot 298 // extract domains from DNS-over-TLS/HTTPS, etc. 299 func ParseDNSQuestion(request []byte) (string, error) { 300 m := new(dns.Msg) 301 err := m.Unpack(request) 302 if err != nil { 303 return "", errors.Trace(err) 304 } 305 if len(m.Question) > 0 { 306 return m.Question[0].Name, nil 307 } 308 return "", nil 309 }