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

lightningnetwork / lnd / 12205625987

06 Dec 2024 08:23PM UTC coverage: 49.763% (-0.04%) from 49.807%
12205625987

Pull #9316

github

ziggie1984
docs: add release-notes.
Pull Request #9316: routing: fix mc blinded path behaviour.

165 of 181 new or added lines in 5 files covered. (91.16%)

238 existing lines in 28 files now uncovered.

100104 of 201163 relevant lines covered (49.76%)

2.07 hits per line

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

62.22
/brontide/listener.go
1
package brontide
2

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

10
        "github.com/lightningnetwork/lnd/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) {
4✔
39

4✔
40
        addr, err := net.ResolveTCPAddr("tcp", listenAddr)
4✔
41
        if err != nil {
4✔
42
                return nil, err
×
43
        }
×
44

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

50
        brontideListener := &Listener{
4✔
51
                localStatic:   localStatic,
4✔
52
                tcp:           l,
4✔
53
                handshakeSema: make(chan struct{}, defaultHandshakes),
4✔
54
                conns:         make(chan maybeConn),
4✔
55
                quit:          make(chan struct{}),
4✔
56
        }
4✔
57

4✔
58
        for i := 0; i < defaultHandshakes; i++ {
8✔
59
                brontideListener.handshakeSema <- struct{}{}
4✔
60
        }
4✔
61

62
        go brontideListener.listen()
4✔
63

4✔
64
        return brontideListener, nil
4✔
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() {
4✔
73
        for {
8✔
74
                select {
4✔
75
                case <-l.handshakeSema:
4✔
76
                case <-l.quit:
4✔
77
                        return
4✔
78
                }
79

80
                conn, err := l.tcp.Accept()
4✔
81
                if err != nil {
8✔
82
                        l.rejectConn(err)
4✔
83
                        l.handshakeSema <- struct{}{}
4✔
84
                        continue
4✔
85
                }
86

87
                go l.doHandshake(conn)
4✔
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.
UNCOV
93
func rejectedConnErr(err error, remoteAddr string) error {
×
UNCOV
94
        return fmt.Errorf("unable to accept connection from %v: %w", remoteAddr,
×
UNCOV
95
                err)
×
UNCOV
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) {
4✔
102
        defer func() { l.handshakeSema <- struct{}{} }()
8✔
103

104
        select {
4✔
105
        case <-l.quit:
×
106
                return
×
107
        default:
4✔
108
        }
109

110
        remoteAddr := conn.RemoteAddr().String()
4✔
111

4✔
112
        brontideConn := &Conn{
4✔
113
                conn:  conn,
4✔
114
                noise: NewBrontideMachine(false, l.localStatic, nil),
4✔
115
        }
4✔
116

4✔
117
        // We'll ensure that we get ActOne from the remote peer in a timely
4✔
118
        // manner. If they don't respond within handshakeReadTimeout, then
4✔
119
        // we'll kill the connection.
4✔
120
        err := conn.SetReadDeadline(time.Now().Add(handshakeReadTimeout))
4✔
121
        if err != nil {
4✔
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
4✔
131
        if _, err := io.ReadFull(conn, actOne[:]); err != nil {
4✔
132
                brontideConn.conn.Close()
×
133
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
134
                return
×
135
        }
×
136
        if err := brontideConn.noise.RecvActOne(actOne); err != nil {
4✔
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()
4✔
145
        if err != nil {
4✔
146
                brontideConn.conn.Close()
×
147
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
148
                return
×
149
        }
×
150
        if _, err := conn.Write(actTwo[:]); err != nil {
4✔
151
                brontideConn.conn.Close()
×
152
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
153
                return
×
154
        }
×
155

156
        select {
4✔
157
        case <-l.quit:
×
158
                return
×
159
        default:
4✔
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))
4✔
166
        if err != nil {
4✔
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
4✔
176
        if _, err := io.ReadFull(conn, actThree[:]); err != nil {
4✔
UNCOV
177
                brontideConn.conn.Close()
×
UNCOV
178
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
UNCOV
179
                return
×
UNCOV
180
        }
×
181
        if err := brontideConn.noise.RecvActThree(actThree); err != nil {
4✔
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{})
4✔
190
        if err != nil {
4✔
191
                brontideConn.conn.Close()
×
192
                l.rejectConn(rejectedConnErr(err, remoteAddr))
×
193
                return
×
194
        }
×
195

196
        l.acceptConn(brontideConn)
4✔
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) {
4✔
208
        select {
4✔
209
        case l.conns <- maybeConn{conn: conn}:
4✔
210
        case <-l.quit:
×
211
        }
212
}
213

214
// rejectConn returns any errors encountered during connection or handshake.
215
func (l *Listener) rejectConn(err error) {
4✔
216
        select {
4✔
UNCOV
217
        case l.conns <- maybeConn{err: err}:
×
218
        case <-l.quit:
4✔
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) {
4✔
230
        select {
4✔
231
        case result := <-l.conns:
4✔
232
                return result.conn, result.err
4✔
233
        case <-l.quit:
4✔
234
                return nil, errors.New("brontide connection closed")
4✔
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 {
4✔
243
        select {
4✔
244
        case <-l.quit:
×
245
        default:
4✔
246
                close(l.quit)
4✔
247
        }
248

249
        return l.tcp.Close()
4✔
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 {
4✔
256
        return l.tcp.Addr()
4✔
257
}
4✔
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