Skip to content

Commit a9872da

Browse files
authored
shttp: allow scion-address in url (#59)
* Allow SCION-addr in URL in shttp/transport Even when having names, it's convenient to be able to use addresses directly. This change allows to use scion-addresses (in the conventional `ISD-AS,[IP]`format) in URLs for shttp.
1 parent 30c6df0 commit a9872da

File tree

1 file changed

+71
-6
lines changed

1 file changed

+71
-6
lines changed

lib/shttp/transport.go

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,19 @@ package shttp
1919

2020
import (
2121
"crypto/tls"
22+
"errors"
23+
"fmt"
2224
"net"
2325
"net/http"
26+
"net/url"
2427
"strconv"
28+
"strings"
2529
"sync"
2630

2731
"github.com/lucas-clemente/quic-go"
2832
"github.com/lucas-clemente/quic-go/h2quic"
2933
"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"
3135
"github.com/scionproto/scion/go/lib/snet"
3236
// "github.com/scionproto/scion/go/lib/snet/squic"
3337
)
@@ -91,9 +95,20 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt h2quic.RoundTripOpt) (*h
9195
return nil, initErr
9296
}
9397

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+
94109
// set the dial function and QuicConfig once for each Transport
95110
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) {
97112

98113
/* TODO(chaehni):
99114
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
105120
*/
106121
cfg.RequestConnectionIDOmission = false
107122

108-
host, port, err := net.SplitHostPort(addr)
123+
host, port, err := net.SplitHostPort(addrStr)
109124
if err != nil {
110125
return nil, err
111126
}
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+
}
113135
if err != nil {
114136
return nil, err
115137
}
116138
p, err := strconv.ParseUint(port, 10, 16)
117139
if err != nil {
118140
p = 443
119141
}
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+
122145
return t.squicDialSCION(nil, t.LAddr, raddr, cfg)
123146
}
124147
t.rt = &h2quic.RoundTripper{
@@ -131,6 +154,48 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt h2quic.RoundTripOpt) (*h
131154
return t.rt.RoundTripOpt(req, opt)
132155
}
133156

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+
134199
// Close closes the QUIC connections that this RoundTripper has used
135200
func (t *Transport) Close() error {
136201
err := t.rt.Close()

0 commit comments

Comments
 (0)