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  }