• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 14358372723

09 Apr 2025 01:26PM UTC coverage: 56.696% (-12.3%) from 69.037%
14358372723

Pull #9696

github

web-flow
Merge e2837e400 into 867d27d68
Pull Request #9696: Add `development_guidelines.md` for both human and machine

107055 of 188823 relevant lines covered (56.7%)

22721.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.23
/brontide/listener.go
1
package brontide
2

3
import (
4
        "errors"
5
        "fmt"
6
        "io"
7
        "net"
8
        "time"
9

10
        "github.com/btcsuite/btcd/btcec/v2"
11
        "github.com/lightningnetwork/lnd/keychain"
12
)
13

14
// defaultHandshakes is the maximum number of handshakes that can be done in
15
// parallel.
16
const defaultHandshakes = 50
17

18
// Listener is an implementation of a net.Conn which executes an authenticated
19
// key exchange and message encryption protocol dubbed "Machine" after
20
// initial connection acceptance. See the Machine struct for additional
21
// details w.r.t the handshake and encryption scheme used within the
22
// connection.
23
type Listener struct {
24
        localStatic keychain.SingleKeyECDH
25

26
        tcp *net.TCPListener
27

28
        // shouldAccept is a closure that determines if we should accept the
29
        // incoming connection or not based on its public key.
30
        shouldAccept func(*btcec.PublicKey) (bool, error)
31

32
        handshakeSema chan struct{}
33
        conns         chan maybeConn
34
        quit          chan struct{}
35
}
36

37
// A compile-time assertion to ensure that Conn meets the net.Listener interface.
38
var _ net.Listener = (*Listener)(nil)
39

40
// NewListener returns a new net.Listener which enforces the Brontide scheme
41
// during both initial connection establishment and data transfer.
42
func NewListener(localStatic keychain.SingleKeyECDH, listenAddr string,
43
        shouldAccept func(*btcec.PublicKey) (bool, error)) (*Listener, error) {
3✔
44

3✔
45
        addr, err := net.ResolveTCPAddr("tcp", listenAddr)
3✔
46
        if err != nil {
3✔
47
                return nil, err
×
48
        }
×
49

50
        l, err := net.ListenTCP("tcp", addr)
3✔
51
        if err != nil {
3✔
52
                return nil, err
×
53
        }
×
54

55
        brontideListener := &Listener{
3✔
56
                localStatic:   localStatic,
3✔
57
                tcp:           l,
3✔
58
                shouldAccept:  shouldAccept,
3✔
59
                handshakeSema: make(chan struct{}, defaultHandshakes),
3✔
60
                conns:         make(chan maybeConn),
3✔
61
                quit:          make(chan struct{}),
3✔
62
        }
3✔
63

3✔
64
        for i := 0; i < defaultHandshakes; i++ {
153✔
65
                brontideListener.handshakeSema <- struct{}{}
150✔
66
        }
150✔
67

68
        go brontideListener.listen()
3✔
69

3✔
70
        return brontideListener, nil
3✔
71
}
72

73
// listen accepts connection from the underlying tcp conn, then performs
74
// the brontinde handshake procedure asynchronously. A maximum of
75
// defaultHandshakes will be active at any given time.
76
//
77
// NOTE: This method must be run as a goroutine.
78
func (l *Listener) listen() {
3✔
79
        for {
20✔
80
                select {
17✔
81
                case <-l.handshakeSema:
14✔
82
                case <-l.quit:
3✔
83
                        return
3✔
84
                }
85

86
                conn, err := l.tcp.Accept()
14✔
87
                if err != nil {
20✔
88
                        l.rejectConn(err)
6✔
89
                        l.handshakeSema <- struct{}{}
6✔
90
                        continue
6✔
91
                }
92

93
                go l.doHandshake(conn)
8✔
94
        }
95
}
96

97
// rejectedConnErr is a helper function that prepends the remote address of the
98
// failed connection attempt to the original error message.
99
func rejectedConnErr(err error, remoteAddr string) error {
5✔
100
        return fmt.Errorf("unable to accept connection from %v: %w", remoteAddr,
5✔
101
                err)
5✔
102
}
5✔
103

104
// doHandshake asynchronously performs the brontide handshake, so that it does
105
// not block the main accept loop. This prevents peers that delay writing to the
106
// connection from block other connection attempts.
107
func (l *Listener) doHandshake(conn net.Conn) {
8✔
108
        defer func() { l.handshakeSema <- struct{}{} }()
16✔
109

110
        select {
8✔
111
        case <-l.quit:
×
112
                return
×
113
        default:
8✔
114
        }
115

116
        remoteAddr := conn.RemoteAddr().String()
8✔
117

8✔
118
        brontideConn := &Conn{
8✔
119
                conn:  conn,
8✔
120
                noise: NewBrontideMachine(false, l.localStatic, nil),
8✔
121
        }
8✔
122

8✔
123
        // We'll ensure that we get ActOne from the remote peer in a timely
8✔
124
        // manner. If they don't respond within handshakeReadTimeout, then
8✔
125
        // we'll kill the connection.
8✔
126
        err := conn.SetReadDeadline(time.Now().Add(handshakeReadTimeout))
8✔
127
        if err != nil {
8✔
128
                brontideConn.conn.Close()
×
129
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
130
                return
×
131
        }
×
132

133
        // Attempt to carry out the first act of the handshake protocol. If the
134
        // connecting node doesn't know our long-term static public key, then
135
        // this portion will fail with a non-nil error.
136
        var actOne [ActOneSize]byte
8✔
137
        if _, err := io.ReadFull(conn, actOne[:]); err != nil {
13✔
138
                brontideConn.conn.Close()
5✔
139
                l.rejectConn(rejectedConnErr(err, remoteAddr))
5✔
140
                return
5✔
141
        }
5✔
142
        if err := brontideConn.noise.RecvActOne(actOne); err != nil {
3✔
143
                brontideConn.conn.Close()
×
144
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
145
                return
×
146
        }
×
147

148
        // Next, progress the handshake processes by sending over our ephemeral
149
        // key for the session along with an authenticating tag.
150
        actTwo, err := brontideConn.noise.GenActTwo()
3✔
151
        if err != nil {
3✔
152
                brontideConn.conn.Close()
×
153
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
154
                return
×
155
        }
×
156
        if _, err := conn.Write(actTwo[:]); err != nil {
3✔
157
                brontideConn.conn.Close()
×
158
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
159
                return
×
160
        }
×
161

162
        select {
3✔
163
        case <-l.quit:
×
164
                return
×
165
        default:
3✔
166
        }
167

168
        // We'll ensure that we get ActTwo from the remote peer in a timely
169
        // manner. If they don't respond within handshakeReadTimeout, then
170
        // we'll kill the connection.
171
        err = conn.SetReadDeadline(time.Now().Add(handshakeReadTimeout))
3✔
172
        if err != nil {
3✔
173
                brontideConn.conn.Close()
×
174
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
175
                return
×
176
        }
×
177

178
        // Finally, finish the handshake processes by reading and decrypting
179
        // the connection peer's static public key. If this succeeds then both
180
        // sides have mutually authenticated each other.
181
        var actThree [ActThreeSize]byte
3✔
182
        if _, err := io.ReadFull(conn, actThree[:]); err != nil {
3✔
183
                brontideConn.conn.Close()
×
184
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
185
                return
×
186
        }
×
187
        if err := brontideConn.noise.RecvActThree(actThree); err != nil {
3✔
188
                brontideConn.conn.Close()
×
189
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
190
                return
×
191
        }
×
192

193
        // We'll reset the deadline as it's no longer critical beyond the
194
        // initial handshake.
195
        err = conn.SetReadDeadline(time.Time{})
3✔
196
        if err != nil {
3✔
197
                brontideConn.conn.Close()
×
198
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
199
                return
×
200
        }
×
201

202
        // Call the shouldAccept closure to see if the remote node's public key
203
        // is allowed according to our banning heuristic. This is here because
204
        // we do not learn the remote node's public static key until we've
205
        // received and validated Act 3.
206
        remoteKey := brontideConn.RemotePub()
3✔
207
        if remoteKey == nil {
3✔
208
                connErr := fmt.Errorf("no remote pubkey")
×
209
                brontideConn.conn.Close()
×
210
                l.rejectConn(rejectedConnErr(connErr, remoteAddr))
×
211

×
212
                return
×
213
        }
×
214

215
        accepted, acceptErr := l.shouldAccept(remoteKey)
3✔
216
        if !accepted {
3✔
217
                // Reject the connection.
×
218
                brontideConn.conn.Close()
×
219
                l.rejectConn(rejectedConnErr(acceptErr, remoteAddr))
×
220

×
221
                return
×
222
        }
×
223

224
        l.acceptConn(brontideConn)
3✔
225
}
226

227
// maybeConn holds either a brontide connection or an error returned from the
228
// handshake.
229
type maybeConn struct {
230
        conn *Conn
231
        err  error
232
}
233

234
// acceptConn returns a connection that successfully performed a handshake.
235
func (l *Listener) acceptConn(conn *Conn) {
3✔
236
        select {
3✔
237
        case l.conns <- maybeConn{conn: conn}:
3✔
238
        case <-l.quit:
×
239
        }
240
}
241

242
// rejectConn returns any errors encountered during connection or handshake.
243
func (l *Listener) rejectConn(err error) {
11✔
244
        select {
11✔
245
        case l.conns <- maybeConn{err: err}:
×
246
        case <-l.quit:
11✔
247
        }
248
}
249

250
// Accept waits for and returns the next connection to the listener. All
251
// incoming connections are authenticated via the three act Brontide
252
// key-exchange scheme. This function will fail with a non-nil error in the
253
// case that either the handshake breaks down, or the remote peer doesn't know
254
// our static public key.
255
//
256
// Part of the net.Listener interface.
257
func (l *Listener) Accept() (net.Conn, error) {
3✔
258
        select {
3✔
259
        case result := <-l.conns:
3✔
260
                return result.conn, result.err
3✔
261
        case <-l.quit:
×
262
                return nil, errors.New("brontide connection closed")
×
263
        }
264
}
265

266
// Close closes the listener.  Any blocked Accept operations will be unblocked
267
// and return errors.
268
//
269
// Part of the net.Listener interface.
270
func (l *Listener) Close() error {
3✔
271
        select {
3✔
272
        case <-l.quit:
×
273
        default:
3✔
274
                close(l.quit)
3✔
275
        }
276

277
        return l.tcp.Close()
3✔
278
}
279

280
// Addr returns the listener's network address.
281
//
282
// Part of the net.Listener interface.
283
func (l *Listener) Addr() net.Addr {
8✔
284
        return l.tcp.Addr()
8✔
285
}
8✔
286

287
// DisabledBanClosure is used in places that NewListener is invoked to bypass
288
// the ban-scoring.
289
func DisabledBanClosure(p *btcec.PublicKey) (bool, error) {
3✔
290
        return true, nil
3✔
291
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc