github.com/decred/dcrlnd@v0.7.6/brontide/listener.go (about)

     1  package brontide
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"time"
     9  
    10  	"github.com/decred/dcrlnd/keychain"
    11  )
    12  
    13  // defaultHandshakes is the maximum number of handshakes that can be done in
    14  // parallel.
    15  const defaultHandshakes = 1000
    16  
    17  // Listener is an implementation of a net.Conn which executes an authenticated
    18  // key exchange and message encryption protocol dubbed "Machine" after
    19  // initial connection acceptance. See the Machine struct for additional
    20  // details w.r.t the handshake and encryption scheme used within the
    21  // connection.
    22  type Listener struct {
    23  	localStatic keychain.SingleKeyECDH
    24  
    25  	tcp *net.TCPListener
    26  
    27  	handshakeSema chan struct{}
    28  	conns         chan maybeConn
    29  	quit          chan struct{}
    30  }
    31  
    32  // A compile-time assertion to ensure that Conn meets the net.Listener interface.
    33  var _ net.Listener = (*Listener)(nil)
    34  
    35  // NewListener returns a new net.Listener which enforces the Brontide scheme
    36  // during both initial connection establishment and data transfer.
    37  func NewListener(localStatic keychain.SingleKeyECDH,
    38  	listenAddr string) (*Listener, error) {
    39  
    40  	addr, err := net.ResolveTCPAddr("tcp", listenAddr)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	l, err := net.ListenTCP("tcp", addr)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	brontideListener := &Listener{
    51  		localStatic:   localStatic,
    52  		tcp:           l,
    53  		handshakeSema: make(chan struct{}, defaultHandshakes),
    54  		conns:         make(chan maybeConn),
    55  		quit:          make(chan struct{}),
    56  	}
    57  
    58  	for i := 0; i < defaultHandshakes; i++ {
    59  		brontideListener.handshakeSema <- struct{}{}
    60  	}
    61  
    62  	go brontideListener.listen()
    63  
    64  	return brontideListener, nil
    65  }
    66  
    67  // listen accepts connection from the underlying tcp conn, then performs
    68  // the brontinde handshake procedure asynchronously. A maximum of
    69  // defaultHandshakes will be active at any given time.
    70  //
    71  // NOTE: This method must be run as a goroutine.
    72  func (l *Listener) listen() {
    73  	for {
    74  		select {
    75  		case <-l.handshakeSema:
    76  		case <-l.quit:
    77  			return
    78  		}
    79  
    80  		conn, err := l.tcp.Accept()
    81  		if err != nil {
    82  			l.rejectConn(err)
    83  			l.handshakeSema <- struct{}{}
    84  			continue
    85  		}
    86  
    87  		go l.doHandshake(conn)
    88  	}
    89  }
    90  
    91  // rejectedConnErr is a helper function that prepends the remote address of the
    92  // failed connection attempt to the original error message.
    93  func rejectedConnErr(err error, remoteAddr string) error {
    94  	return fmt.Errorf("unable to accept connection from %v: %v", remoteAddr,
    95  		err)
    96  }
    97  
    98  // doHandshake asynchronously performs the brontide handshake, so that it does
    99  // not block the main accept loop. This prevents peers that delay writing to the
   100  // connection from block other connection attempts.
   101  func (l *Listener) doHandshake(conn net.Conn) {
   102  	defer func() { l.handshakeSema <- struct{}{} }()
   103  
   104  	select {
   105  	case <-l.quit:
   106  		return
   107  	default:
   108  	}
   109  
   110  	remoteAddr := conn.RemoteAddr().String()
   111  
   112  	brontideConn := &Conn{
   113  		conn:  conn,
   114  		noise: NewBrontideMachine(false, l.localStatic, nil),
   115  	}
   116  
   117  	// We'll ensure that we get ActOne from the remote peer in a timely
   118  	// manner. If they don't respond within handshakeReadTimeout, then
   119  	// we'll kill the connection.
   120  	err := conn.SetReadDeadline(time.Now().Add(handshakeReadTimeout))
   121  	if err != nil {
   122  		brontideConn.conn.Close()
   123  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   124  		return
   125  	}
   126  
   127  	// Attempt to carry out the first act of the handshake protocol. If the
   128  	// connecting node doesn't know our long-term static public key, then
   129  	// this portion will fail with a non-nil error.
   130  	var actOne [ActOneSize]byte
   131  	if _, err := io.ReadFull(conn, actOne[:]); err != nil {
   132  		brontideConn.conn.Close()
   133  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   134  		return
   135  	}
   136  	if err := brontideConn.noise.RecvActOne(actOne); err != nil {
   137  		brontideConn.conn.Close()
   138  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   139  		return
   140  	}
   141  
   142  	// Next, progress the handshake processes by sending over our ephemeral
   143  	// key for the session along with an authenticating tag.
   144  	actTwo, err := brontideConn.noise.GenActTwo()
   145  	if err != nil {
   146  		brontideConn.conn.Close()
   147  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   148  		return
   149  	}
   150  	if _, err := conn.Write(actTwo[:]); err != nil {
   151  		brontideConn.conn.Close()
   152  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   153  		return
   154  	}
   155  
   156  	select {
   157  	case <-l.quit:
   158  		return
   159  	default:
   160  	}
   161  
   162  	// We'll ensure that we get ActTwo from the remote peer in a timely
   163  	// manner. If they don't respond within handshakeReadTimeout, then
   164  	// we'll kill the connection.
   165  	err = conn.SetReadDeadline(time.Now().Add(handshakeReadTimeout))
   166  	if err != nil {
   167  		brontideConn.conn.Close()
   168  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   169  		return
   170  	}
   171  
   172  	// Finally, finish the handshake processes by reading and decrypting
   173  	// the connection peer's static public key. If this succeeds then both
   174  	// sides have mutually authenticated each other.
   175  	var actThree [ActThreeSize]byte
   176  	if _, err := io.ReadFull(conn, actThree[:]); err != nil {
   177  		brontideConn.conn.Close()
   178  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   179  		return
   180  	}
   181  	if err := brontideConn.noise.RecvActThree(actThree); err != nil {
   182  		brontideConn.conn.Close()
   183  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   184  		return
   185  	}
   186  
   187  	// We'll reset the deadline as it's no longer critical beyond the
   188  	// initial handshake.
   189  	err = conn.SetReadDeadline(time.Time{})
   190  	if err != nil {
   191  		brontideConn.conn.Close()
   192  		l.rejectConn(rejectedConnErr(err, remoteAddr))
   193  		return
   194  	}
   195  
   196  	l.acceptConn(brontideConn)
   197  }
   198  
   199  // maybeConn holds either a brontide connection or an error returned from the
   200  // handshake.
   201  type maybeConn struct {
   202  	conn *Conn
   203  	err  error
   204  }
   205  
   206  // acceptConn returns a connection that successfully performed a handshake.
   207  func (l *Listener) acceptConn(conn *Conn) {
   208  	select {
   209  	case l.conns <- maybeConn{conn: conn}:
   210  	case <-l.quit:
   211  	}
   212  }
   213  
   214  // rejectConn returns any errors encountered during connection or handshake.
   215  func (l *Listener) rejectConn(err error) {
   216  	select {
   217  	case l.conns <- maybeConn{err: err}:
   218  	case <-l.quit:
   219  	}
   220  }
   221  
   222  // Accept waits for and returns the next connection to the listener. All
   223  // incoming connections are authenticated via the three act Brontide
   224  // key-exchange scheme. This function will fail with a non-nil error in the
   225  // case that either the handshake breaks down, or the remote peer doesn't know
   226  // our static public key.
   227  //
   228  // Part of the net.Listener interface.
   229  func (l *Listener) Accept() (net.Conn, error) {
   230  	select {
   231  	case result := <-l.conns:
   232  		return result.conn, result.err
   233  	case <-l.quit:
   234  		return nil, errors.New("brontide connection closed")
   235  	}
   236  }
   237  
   238  // Close closes the listener.  Any blocked Accept operations will be unblocked
   239  // and return errors.
   240  //
   241  // Part of the net.Listener interface.
   242  func (l *Listener) Close() error {
   243  	select {
   244  	case <-l.quit:
   245  	default:
   246  		close(l.quit)
   247  	}
   248  
   249  	return l.tcp.Close()
   250  }
   251  
   252  // Addr returns the listener's network address.
   253  //
   254  // Part of the net.Listener interface.
   255  func (l *Listener) Addr() net.Addr {
   256  	return l.tcp.Addr()
   257  }