Skip to content

Commit 76e8e61

Browse files
authored
fix #2227 (#2334)
* fix #2227 Instead of returning an error from Accept(), force the caller to process errors from trying to read and parse the PROXY protocol. The advantage here is that we don't have to rely on (net.Error).Temporary or incur timed backoff from net/http when hitting these errors. However, we still risk stalling processing of new incoming connections if someone opens a connection to the proxy listener and doesn't send anything. This is hard to fix while maintaining the net.Listener abstraction in cooperation with http.Server. * reduce proxy deadline to 5 seconds
1 parent b120806 commit 76e8e61

File tree

3 files changed

+34
-37
lines changed

3 files changed

+34
-37
lines changed

irc/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import (
4646
)
4747

4848
const (
49-
defaultProxyDeadline = time.Minute
49+
defaultProxyDeadline = 5 * time.Second
5050
)
5151

5252
// here's how this works: exported (capitalized) members of the config structs

irc/listeners.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,13 @@ func (nl *NetListener) serve() {
9999
// hand off the connection
100100
wConn, ok := conn.(*utils.WrappedConn)
101101
if ok {
102-
confirmProxyData(wConn, "", "", "", nl.server.Config())
103-
go nl.server.RunClient(NewIRCStreamConn(wConn))
102+
if wConn.ProxyError == nil {
103+
confirmProxyData(wConn, "", "", "", nl.server.Config())
104+
go nl.server.RunClient(NewIRCStreamConn(wConn))
105+
} else {
106+
nl.server.logger.Error("internal", "PROXY protocol error", nl.addr, wConn.ProxyError.Error())
107+
conn.Close()
108+
}
104109
} else {
105110
nl.server.logger.Error("internal", "invalid connection type", nl.addr)
106111
}
@@ -185,6 +190,13 @@ func (wl *WSListener) handle(w http.ResponseWriter, r *http.Request) {
185190
conn.Close()
186191
return
187192
}
193+
if wConn.ProxyError != nil {
194+
// actually the connection is likely corrupted, so probably Upgrade()
195+
// would have already failed
196+
wl.server.logger.Error("internal", "PROXY protocol error on websocket", wl.addr, wConn.ProxyError.Error())
197+
conn.Close()
198+
return
199+
}
188200

189201
confirmProxyData(wConn, remoteAddr, xff, xfp, config)
190202

irc/utils/proxy.go

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package utils
66
import (
77
"crypto/tls"
88
"encoding/binary"
9+
"errors"
910
"io"
1011
"net"
1112
"strings"
@@ -20,24 +21,8 @@ const (
2021
maxProxyLineLenV1 = 107
2122
)
2223

23-
// XXX implement net.Error with a Temporary() method that returns true;
24-
// otherwise, ErrBadProxyLine will cause (*http.Server).Serve() to exit
25-
type proxyLineError struct{}
26-
27-
func (p *proxyLineError) Error() string {
28-
return "invalid PROXY line"
29-
}
30-
31-
func (p *proxyLineError) Timeout() bool {
32-
return false
33-
}
34-
35-
func (p *proxyLineError) Temporary() bool {
36-
return true
37-
}
38-
3924
var (
40-
ErrBadProxyLine error = &proxyLineError{}
25+
ErrBadProxyLine = errors.New("invalid PROXY protocol line")
4126
)
4227

4328
// ListenerConfig is all the information about how to process
@@ -208,12 +193,13 @@ func parseProxyLineV2(line []byte) (ip net.IP, err error) {
208193
// configuration.
209194
type WrappedConn struct {
210195
net.Conn
211-
ProxiedIP net.IP
212-
TLS bool
213-
Tor bool
214-
STSOnly bool
215-
WebSocket bool
216-
HideSTS bool
196+
ProxiedIP net.IP
197+
ProxyError error
198+
TLS bool
199+
Tor bool
200+
STSOnly bool
201+
WebSocket bool
202+
HideSTS bool
217203
// Secure indicates whether we believe the connection between us and the client
218204
// was secure against interception and modification (including all proxies):
219205
Secure bool
@@ -256,6 +242,7 @@ func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
256242
}
257243

258244
var proxiedIP net.IP
245+
var proxyError error
259246
if config.RequireProxy {
260247
// this will occur synchronously on the goroutine calling Accept(),
261248
// but that's OK because this listener *requires* a PROXY line,
@@ -265,24 +252,22 @@ func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
265252
if err == nil {
266253
proxiedIP, err = ParseProxyLine(proxyLine)
267254
}
268-
if err != nil {
269-
conn.Close()
270-
return nil, err
271-
}
255+
proxyError = err
272256
}
273257

274258
if config.TLSConfig != nil {
275259
conn = tls.Server(conn, config.TLSConfig)
276260
}
277261

278262
return &WrappedConn{
279-
Conn: conn,
280-
ProxiedIP: proxiedIP,
281-
TLS: config.TLSConfig != nil,
282-
Tor: config.Tor,
283-
STSOnly: config.STSOnly,
284-
WebSocket: config.WebSocket,
285-
HideSTS: config.HideSTS,
263+
Conn: conn,
264+
ProxiedIP: proxiedIP,
265+
ProxyError: proxyError,
266+
TLS: config.TLSConfig != nil,
267+
Tor: config.Tor,
268+
STSOnly: config.STSOnly,
269+
WebSocket: config.WebSocket,
270+
HideSTS: config.HideSTS,
286271
// Secure will be set later by client code
287272
}, nil
288273
}

0 commit comments

Comments
 (0)