@@ -19,15 +19,19 @@ package shttp
19
19
20
20
import (
21
21
"crypto/tls"
22
+ "errors"
23
+ "fmt"
22
24
"net"
23
25
"net/http"
26
+ "net/url"
24
27
"strconv"
28
+ "strings"
25
29
"sync"
26
30
27
31
"github.com/lucas-clemente/quic-go"
28
32
"github.com/lucas-clemente/quic-go/h2quic"
29
33
"github.com/netsec-ethz/scion-apps/lib/scionutil"
30
- libaddr "github.com/scionproto/scion/go/lib/addr"
34
+ "github.com/scionproto/scion/go/lib/addr"
31
35
"github.com/scionproto/scion/go/lib/snet"
32
36
// "github.com/scionproto/scion/go/lib/snet/squic"
33
37
)
@@ -91,9 +95,20 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt h2quic.RoundTripOpt) (*h
91
95
return nil , initErr
92
96
}
93
97
98
+ // If req.URL.Host is a SCION address, we need to mangle it so it passes through
99
+ // h2quic without tripping up.
100
+ raddr , err := snet .AddrFromString (req .URL .Host )
101
+ if err == nil {
102
+ tmp := * req
103
+ tmp .URL = new (url.URL )
104
+ * tmp .URL = * req .URL
105
+ tmp .URL .Host = mangleSCIONAddr (raddr )
106
+ req = & tmp
107
+ }
108
+
94
109
// set the dial function and QuicConfig once for each Transport
95
110
t .dialOnce .Do (func () {
96
- dial := func (network , addr string , tlsCfg * tls.Config , cfg * quic.Config ) (quic.Session , error ) {
111
+ dial := func (network , addrStr string , tlsCfg * tls.Config , cfg * quic.Config ) (quic.Session , error ) {
97
112
98
113
/* TODO(chaehni):
99
114
RequestConnectionIDOmission MUST not be set to 'true' when a connection is dialed using an existing net.PacketConn
@@ -105,20 +120,28 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt h2quic.RoundTripOpt) (*h
105
120
*/
106
121
cfg .RequestConnectionIDOmission = false
107
122
108
- host , port , err := net .SplitHostPort (addr )
123
+ host , port , err := net .SplitHostPort (addrStr )
109
124
if err != nil {
110
125
return nil , err
111
126
}
112
- ia , l3 , err := scionutil .GetHostByName (host )
127
+
128
+ var ia addr.IA
129
+ var l3 addr.HostAddr
130
+ if isMangledSCIONAddr (host ) {
131
+ ia , l3 , err = unmangleSCIONAddr (host )
132
+ } else {
133
+ ia , l3 , err = scionutil .GetHostByName (host )
134
+ }
113
135
if err != nil {
114
136
return nil , err
115
137
}
116
138
p , err := strconv .ParseUint (port , 10 , 16 )
117
139
if err != nil {
118
140
p = 443
119
141
}
120
- l4 := libaddr .NewL4UDPInfo (uint16 (p ))
121
- raddr := & snet.Addr {IA : ia , Host : & libaddr.AppAddr {L3 : l3 , L4 : l4 }}
142
+ l4 := addr .NewL4UDPInfo (uint16 (p ))
143
+ raddr := & snet.Addr {IA : ia , Host : & addr.AppAddr {L3 : l3 , L4 : l4 }}
144
+
122
145
return t .squicDialSCION (nil , t .LAddr , raddr , cfg )
123
146
}
124
147
t .rt = & h2quic.RoundTripper {
@@ -131,6 +154,48 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt h2quic.RoundTripOpt) (*h
131
154
return t .rt .RoundTripOpt (req , opt )
132
155
}
133
156
157
+ // mangleSCIONAddr encodes the given SCION address so that it can be safely
158
+ // used in the host part of a URL.
159
+ func mangleSCIONAddr (raddr * snet.Addr ) string {
160
+
161
+ // The HostAddr will be either IPv4 or IPv6 (not a HostSVC-addr).
162
+ // To make this a valid host string for a URL, replace : for IPv6 addresses by ~. There will
163
+ // not be any other tildes, so no need to escape them.
164
+ l3 := raddr .Host .L3 .String ()
165
+ l3_mangled := strings .Replace (l3 , ":" , "~" , - 1 )
166
+
167
+ u := fmt .Sprintf ("__%s__%s__" , raddr .IA .FileFmt (false ), l3_mangled )
168
+ if raddr .Host .L4 != nil {
169
+ u += fmt .Sprintf (":%d" , raddr .Host .L4 .Port ())
170
+ }
171
+ return u
172
+ }
173
+
174
+ // isMangledSCIONAddr checks if this is an address previously encoded with mangleSCIONAddr
175
+ // without port, *after* SplitHostPort has been applied.
176
+ func isMangledSCIONAddr (host string ) bool {
177
+
178
+ parts := strings .Split (host , "__" )
179
+ return len (parts ) == 4 && len (parts [0 ]) == 0 && len (parts [3 ]) == 0
180
+ }
181
+
182
+ // unmangleSCIONAddr decodes and parses a SCION-address previously encoded with mangleSCIONAddr
183
+ // without port, i.e. *after* SplitHostPort has been applied.
184
+ func unmangleSCIONAddr (host string ) (addr.IA , addr.HostAddr , error ) {
185
+
186
+ parts := strings .Split (host , "__" )
187
+ ia , err := addr .IAFromFileFmt (parts [1 ], false )
188
+ if err != nil {
189
+ return addr.IA {}, nil , err
190
+ }
191
+ l3_str := strings .Replace (parts [2 ], "~" , ":" , - 1 )
192
+ l3 := addr .HostFromIPStr (l3_str )
193
+ if l3 == nil {
194
+ return addr.IA {}, nil , errors .New ("Could not parse IP in SCION-address" )
195
+ }
196
+ return ia , l3 , nil
197
+ }
198
+
134
199
// Close closes the QUIC connections that this RoundTripper has used
135
200
func (t * Transport ) Close () error {
136
201
err := t .rt .Close ()
0 commit comments