Skip to content

Commit c69902f

Browse files
committed
Workaround squic: explicitly close connections
Due to behavior changes in quic-go, any session created with squic.DialSCION will leak the underlying snet.Conn -- the quic.Session will simply not call Close on a connection passed in. Re-implement the small squic.DialSCION functions to get access to the snet.Conn to close them later.
1 parent bcfb86d commit c69902f

File tree

1 file changed

+44
-5
lines changed

1 file changed

+44
-5
lines changed

lib/shttp/transport.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ import (
2424
"strconv"
2525
"sync"
2626

27-
quic "github.com/lucas-clemente/quic-go"
27+
"github.com/lucas-clemente/quic-go"
2828
"github.com/lucas-clemente/quic-go/h2quic"
2929
"github.com/netsec-ethz/scion-apps/lib/scionutil"
3030
libaddr "github.com/scionproto/scion/go/lib/addr"
3131
"github.com/scionproto/scion/go/lib/snet"
32-
"github.com/scionproto/scion/go/lib/snet/squic"
32+
// "github.com/scionproto/scion/go/lib/snet/squic"
33+
)
34+
35+
var (
36+
// cliTlsCfg is a copy squic.cliTlsCfg
37+
cliTlsCfg = &tls.Config{InsecureSkipVerify: true}
3338
)
3439

3540
// Transport wraps a h2quic.RoundTripper making it compatible with SCION
@@ -40,7 +45,9 @@ type Transport struct {
4045

4146
rt *h2quic.RoundTripper
4247

43-
dialOnce sync.Once
48+
dialOnce sync.Once
49+
connectionsMutex sync.Mutex
50+
connections []*snet.Conn
4451
}
4552

4653
// RoundTrip does a single round trip; retreiving a response for a given request
@@ -49,6 +56,28 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
4956
return t.RoundTripOpt(req, h2quic.RoundTripOpt{})
5057
}
5158

59+
// squicDialSCION re-implements squic.DialSCION to get access to the underlying
60+
// snet.Conn to be able to close the connection later.
61+
func (t *Transport) squicDialSCION(network *snet.SCIONNetwork, laddr, raddr *snet.Addr,
62+
quicConfig *quic.Config) (quic.Session, error) {
63+
64+
// squic.sListen (but without the Bind/SVC)
65+
if network == nil {
66+
network = snet.DefNetwork
67+
}
68+
sconn, err := network.ListenSCION("udp4", laddr, 0)
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
t.connectionsMutex.Lock()
74+
defer t.connectionsMutex.Unlock()
75+
t.connections = append(t.connections, &sconn)
76+
77+
// Use dummy hostname, as it's used for SNI, and we're not doing cert verification.
78+
return quic.Dial(sconn, raddr, "host:0", cliTlsCfg, quicConfig)
79+
}
80+
5281
// RoundTripOpt is the same as RoundTrip but takes additional options
5382
func (t *Transport) RoundTripOpt(req *http.Request, opt h2quic.RoundTripOpt) (*http.Response, error) {
5483

@@ -90,7 +119,7 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt h2quic.RoundTripOpt) (*h
90119
}
91120
l4 := libaddr.NewL4UDPInfo(uint16(p))
92121
raddr := &snet.Addr{IA: ia, Host: &libaddr.AppAddr{L3: l3, L4: l4}}
93-
return squic.DialSCION(nil, t.LAddr, raddr, cfg)
122+
return t.squicDialSCION(nil, t.LAddr, raddr, cfg)
94123
}
95124
t.rt = &h2quic.RoundTripper{
96125
Dial: dial,
@@ -104,5 +133,15 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt h2quic.RoundTripOpt) (*h
104133

105134
// Close closes the QUIC connections that this RoundTripper has used
106135
func (t *Transport) Close() error {
107-
return t.rt.Close()
136+
err := t.rt.Close()
137+
138+
// quic.Session.Close (which is called by RoundTripper.Close()) will NOT
139+
// close the underlying connections, so we do it manually here.
140+
t.connectionsMutex.Lock()
141+
defer t.connectionsMutex.Unlock()
142+
for _, sconn := range t.connections {
143+
(*sconn).Close()
144+
}
145+
146+
return err
108147
}

0 commit comments

Comments
 (0)