-
Notifications
You must be signed in to change notification settings - Fork 45
/
Copy pathappquic.go
171 lines (154 loc) · 5.42 KB
/
appquic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright 2019 ETH Zurich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package appquic provides a simple interface to use QUIC over SCION.
// This package is similar to snet/squic, but offers a smoother interface for
// applications and, like appnet, it allows to Dial hostnames resolved with RAINS.
package appquic
import (
"crypto/tls"
"fmt"
"sync"
"github.com/lucas-clemente/quic-go"
"github.com/netsec-ethz/scion-apps/pkg/appnet"
"github.com/scionproto/scion/go/lib/snet"
)
var (
srvTLSDummyCerts []tls.Certificate
srvTLSDummyCertsInit sync.Once
)
// closerSession is a wrapper around quic.Session that always closes the
// underlying sconn when closing the session.
// This is needed here because we use quic.Dial, not quic.DialAddr but we want
// the close-the-socket behaviour of quic.DialAddr.
type closerSession struct {
quic.Session
conn *snet.Conn
}
func (s *closerSession) CloseWithError(code quic.ErrorCode, desc string) error {
s.conn.Close()
return s.Session.CloseWithError(code, desc)
}
// closerEarlySession is a wrapper around quic.EarlySession, analogous to closerSession
type closerEarlySession struct {
quic.EarlySession
conn *snet.Conn
}
func (s *closerEarlySession) CloseWithError(code quic.ErrorCode, desc string) error {
s.conn.Close()
return s.EarlySession.CloseWithError(code, desc)
}
// Dial establishes a new QUIC connection to a server at the remote address.
// The address can be of the form of a SCION address (i.e. of the form "ISD-AS,[IP]:port")
// or in the form of hostname:port.
func Dial(remote string, tlsConf *tls.Config, quicConf *quic.Config) (quic.Session, error) {
raddr, err := appnet.ResolveUDPAddr(remote)
if err != nil {
return nil, err
}
if tlsConf.ServerName == "" {
tlsConf.ServerName = mangleSCIONAddr(raddr)
}
return DialAddr(raddr, remote, tlsConf, quicConf)
}
// DialAddr establishes a new QUIC connection to a server at the remote address.
//
// If no path is specified in raddr, DialAddr will choose the first available path,
// analogous to appnet.DialAddr.
// The host parameter is used for SNI.
// The tls.Config must define an application protocol (using NextProtos).
func DialAddr(raddr *snet.UDPAddr, host string, tlsConf *tls.Config, quicConf *quic.Config) (quic.Session, error) {
err := ensurePathDefined(raddr)
if err != nil {
return nil, err
}
sconn, err := appnet.Listen(nil)
if err != nil {
return nil, err
}
session, err := quic.Dial(sconn, raddr, host, tlsConf, quicConf)
if err != nil {
return nil, err
}
return &closerSession{session, sconn}, nil
}
// DialEarly establishes a new 0-RTT QUIC connection to a server. Analogous to Dial.
func DialEarly(remote string, tlsConf *tls.Config, quicConf *quic.Config) (quic.EarlySession, error) {
raddr, err := appnet.ResolveUDPAddr(remote)
if err != nil {
return nil, err
}
if tlsConf.ServerName == "" {
tlsConf.ServerName = mangleSCIONAddr(raddr)
}
return DialAddrEarly(raddr, remote, tlsConf, quicConf)
}
// DialAddrEarly establishes a new 0-RTT QUIC connection to a server. Analogous to DialAddr.
func DialAddrEarly(raddr *snet.UDPAddr, host string, tlsConf *tls.Config, quicConf *quic.Config) (quic.EarlySession, error) {
err := ensurePathDefined(raddr)
if err != nil {
return nil, err
}
sconn, err := appnet.Listen(nil)
if err != nil {
return nil, err
}
session, err := quic.DialEarly(sconn, raddr, host, tlsConf, quicConf)
if err != nil {
return nil, err
}
// XXX(matzf): quic.DialEarly seems to have the wrong return type declared (quic.DialAddrEarly returns EarlySession)
return &closerEarlySession{session.(quic.EarlySession), sconn}, nil
}
func ensurePathDefined(raddr *snet.UDPAddr) error {
if raddr.Path == nil {
return appnet.SetDefaultPath(raddr)
}
return nil
}
// mangleSCIONAddr mangles a SCION address
func mangleSCIONAddr(raddr *snet.UDPAddr) string {
// Turn this into [IA,IP]:port format.
// Same mangling as for the host part in MangleSCIONAddrURL github.com/netsec-ethz/scion-apps/pkg/shttp/transport.go
mangledAddr := fmt.Sprintf("[%s,%s]", raddr.IA, raddr.Host.IP)
if raddr.Host.Port != 0 {
mangledAddr += fmt.Sprintf(":%d", raddr.Host.Port)
}
return mangledAddr
}
// ListenPort listens for QUIC connections on a SCION/UDP port.
//
// See note on wildcard addresses in the appnet package documentation.
func ListenPort(port uint16, tlsConf *tls.Config, quicConfig *quic.Config) (quic.Listener, error) {
sconn, err := appnet.ListenPort(port)
if err != nil {
return nil, err
}
return quic.Listen(sconn, tlsConf, quicConfig)
}
// GetDummyTLSCert returns the singleton TLS certificate with a fresh
// private key and a dummy certificate.
func GetDummyTLSCerts() []tls.Certificate {
var initErr error
srvTLSDummyCertsInit.Do(func() {
cert, err := generateKeyAndCert()
if err != nil {
initErr = fmt.Errorf("appquic: Unable to generate dummy TLS cert/key: %v", err)
}
srvTLSDummyCerts = []tls.Certificate{*cert}
})
if initErr != nil {
panic(initErr)
}
return srvTLSDummyCerts
}