Skip to content

Commit a8ebd2d

Browse files
committed
pkg/shttp[3], bat, skip: convert web demo tools to pan
Convert pkg/shttp, pkg/shttp3, bat, skip, and web-gateway to use pan. The shttp package now uses the quicutil.SingleStream, making it incompatible with previous versions. The shttp and shttp3 libraries now allow specifying a pan Policy. This Policy can be changed at any time. Add new path policy commandline options to bat.
1 parent ad365a1 commit a8ebd2d

File tree

8 files changed

+165
-109
lines changed

8 files changed

+165
-109
lines changed

bat/bat.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"strconv"
3737
"strings"
3838

39+
"github.com/netsec-ethz/scion-apps/pkg/pan"
3940
"github.com/netsec-ethz/scion-apps/pkg/shttp"
4041
)
4142

@@ -49,6 +50,8 @@ const (
4950

5051
var (
5152
interactive bool
53+
sequence string
54+
preference string
5255
ver bool
5356
form bool
5457
pretty bool
@@ -70,7 +73,11 @@ var (
7073
)
7174

7275
func init() {
73-
flag.BoolVar(&interactive, "in", false, "Lets user choose the path to destination")
76+
flag.BoolVar(&interactive, "interactive", false, "Prompt user for interactive path selection")
77+
flag.StringVar(&sequence, "sequence", "", "Sequence of space separated hop predicates to specify path")
78+
flag.StringVar(&preference, "preference", "", "Preference sorting order for paths. "+
79+
"Comma-separated list of available sorting options: "+
80+
strings.Join(pan.AvailablePreferencePolicies, "|"))
7481
flag.BoolVar(&ver, "v", false, "Print Version Number")
7582
flag.BoolVar(&ver, "version", false, "Print Version Number")
7683
flag.BoolVar(&pretty, "pretty", true, "Print Json Pretty Format")
@@ -97,7 +104,11 @@ func init() {
97104
flag.Usage = usage
98105
flag.Parse()
99106

100-
defaultSetting.Transport = shttp.DefaultTransport
107+
policy, err := pan.PolicyFromCommandline(sequence, preference, interactive)
108+
if err != nil {
109+
log.Fatal(err)
110+
}
111+
defaultSetting.Transport, _ = shttp.NewTransport(nil, policy)
101112
}
102113

103114
func parsePrintOption(s string) {

pkg/shttp/server.go

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@ import (
2020
"net"
2121
"net/http"
2222

23-
"github.com/lucas-clemente/quic-go"
24-
"github.com/netsec-ethz/scion-apps/pkg/appnet/appquic"
23+
"github.com/netsec-ethz/scion-apps/pkg/pan"
24+
"github.com/netsec-ethz/scion-apps/pkg/quicutil"
2525
)
2626

27-
const nextProtoRaw = "raw" // Used for pretend-its-TCP QUIC
28-
2927
// Server wraps a http.Server making it work with SCION
3028
type Server struct {
3129
*http.Server
@@ -87,40 +85,16 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
8785

8886
func listen(addr string) (net.Listener, error) {
8987
tlsCfg := &tls.Config{
90-
NextProtos: []string{nextProtoRaw},
91-
Certificates: appquic.GetDummyTLSCerts(),
92-
}
93-
laddr, err := net.ResolveUDPAddr("udp", addr)
94-
if err != nil {
95-
return nil, err
88+
NextProtos: []string{quicutil.SingleStreamProto},
89+
Certificates: quicutil.MustGenerateSelfSignedCert(),
9690
}
97-
quicListener, err := appquic.Listen(laddr, tlsCfg, nil)
91+
laddr, err := pan.ParseOptionalIPPort(addr)
9892
if err != nil {
9993
return nil, err
10094
}
101-
return singleStreamListener{quicListener}, nil
102-
}
103-
104-
type singleStreamListener struct {
105-
quic.Listener
106-
}
107-
108-
func (l singleStreamListener) Accept() (net.Conn, error) {
109-
ctx := context.Background()
110-
sess, err := l.Listener.Accept(ctx)
95+
quicListener, err := pan.ListenQUIC(context.Background(), laddr, nil, tlsCfg, nil)
11196
if err != nil {
11297
return nil, err
11398
}
114-
str, err := sess.AcceptStream(ctx)
115-
return singleStreamSession{sess, str}, err
116-
}
117-
118-
type singleStreamSession struct {
119-
quic.Session
120-
quic.Stream
121-
}
122-
123-
func (s singleStreamSession) Close() error {
124-
s.Stream.Close()
125-
return s.Session.CloseWithError(0, "")
99+
return quicutil.SingleStreamListener{Listener: quicListener}, nil
126100
}

pkg/shttp/transport.go

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import (
2424
"time"
2525

2626
"github.com/lucas-clemente/quic-go"
27-
"github.com/netsec-ethz/scion-apps/pkg/appnet"
28-
"github.com/netsec-ethz/scion-apps/pkg/appnet/appquic"
27+
"inet.af/netaddr"
28+
29+
"github.com/netsec-ethz/scion-apps/pkg/pan"
30+
"github.com/netsec-ethz/scion-apps/pkg/quicutil"
2931
)
3032

3133
// DefaultTransport is the default RoundTripper that can be used for HTTP over
@@ -36,6 +38,7 @@ var DefaultTransport = &http.Transport{
3638
Proxy: http.ProxyFromEnvironment,
3739
DialContext: (&Dialer{
3840
QuicConfig: nil,
41+
Policy: nil,
3942
}).DialContext,
4043
ForceAttemptHTTP2: true,
4144
MaxIdleConns: 100,
@@ -44,28 +47,56 @@ var DefaultTransport = &http.Transport{
4447
ExpectContinueTimeout: 1 * time.Second,
4548
}
4649

50+
// NewTransport creates a new RoundTripper that can be used for HTTP over
51+
// SCION/QUIC, providing a custom QuicConfig and Policy to the Dialer.
52+
// This equivalent to net/http.DefaultTransport with an overridden DialContext.
53+
// Both the Transport and the Dialer are returned, as the Dialer is not otherwise
54+
// accessible from the Transport.
55+
func NewTransport(quicCfg *quic.Config, policy pan.Policy) (*http.Transport, *Dialer) {
56+
dialer := &Dialer{
57+
QuicConfig: quicCfg,
58+
Policy: policy,
59+
}
60+
transport := DefaultTransport.Clone()
61+
transport.DialContext = dialer.DialContext
62+
return transport, dialer
63+
}
64+
4765
// Dialer dials an insecure, single-stream QUIC connection over SCION (just pretend it's TCP).
4866
// This is the Dialer used for shttp.DefaultTransport.
4967
type Dialer struct {
68+
Local netaddr.IPPort
5069
QuicConfig *quic.Config
70+
Policy pan.Policy
71+
sessions []*pan.QUICSession
5172
}
5273

5374
// DialContext dials an insecure, single-stream QUIC connection over SCION. This can be used
5475
// as the DialContext function in net/http.Transport.
5576
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
5677
tlsCfg := &tls.Config{
57-
NextProtos: []string{nextProtoRaw},
78+
NextProtos: []string{quicutil.SingleStreamProto},
5879
InsecureSkipVerify: true,
5980
}
60-
sess, err := appquic.Dial(appnet.UnmangleSCIONAddr(addr), tlsCfg, d.QuicConfig)
81+
82+
remote, err := pan.ResolveUDPAddr(pan.UnmangleSCIONAddr(addr))
6183
if err != nil {
6284
return nil, err
6385
}
64-
stream, err := sess.OpenStreamSync(ctx)
86+
87+
session, err := pan.DialQUIC(ctx, d.Local, remote, d.Policy, nil, addr, tlsCfg, d.QuicConfig)
6588
if err != nil {
6689
return nil, err
6790
}
68-
return singleStreamSession{sess, stream}, nil
91+
d.sessions = append(d.sessions, session)
92+
return quicutil.NewSingleStream(session)
93+
}
94+
95+
func (d *Dialer) SetPolicy(policy pan.Policy) {
96+
d.Policy = policy
97+
for _, s := range d.sessions {
98+
s.Conn.SetPolicy(policy)
99+
}
69100
}
70101

71102
var scionAddrURLRegexp = regexp.MustCompile(
@@ -84,5 +115,5 @@ func MangleSCIONAddrURL(url string) string {
84115
userInfoPart := match[2]
85116
hostPart := match[3]
86117
tail := match[4]
87-
return schemePart + userInfoPart + appnet.MangleSCIONAddr(hostPart) + tail
118+
return schemePart + userInfoPart + pan.MangleSCIONAddr(hostPart) + tail
88119
}

pkg/shttp/transport_test.go

Lines changed: 32 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ import (
2121
"net"
2222
"net/http"
2323
"net/url"
24+
"strconv"
2425
"strings"
2526
"testing"
2627

27-
"github.com/netsec-ethz/scion-apps/pkg/appnet"
28-
"github.com/scionproto/scion/go/lib/addr"
29-
"github.com/scionproto/scion/go/lib/snet"
28+
"github.com/stretchr/testify/assert"
29+
"github.com/stretchr/testify/require"
30+
"inet.af/netaddr"
31+
32+
"github.com/netsec-ethz/scion-apps/pkg/pan"
3033
)
3134

3235
func TestMangleSCIONAddrURL(t *testing.T) {
@@ -63,35 +66,15 @@ func TestMangleSCIONAddrURL(t *testing.T) {
6366
if _, _, err := net.SplitHostPort(u.Host); err != nil {
6467
continue
6568
}
66-
unmangled := appnet.UnmangleSCIONAddr(u.Host)
69+
unmangled := pan.UnmangleSCIONAddr(u.Host)
6770
if unmangled != tc.HostPort {
6871
t.Fatalf("UnmangleSCIONAddr('%s') returned different result, actual='%s', expected='%s'", u.Host, unmangled, tc.HostPort)
6972
}
7073
}
7174
}
7275
}
7376

74-
type mockResolver struct {
75-
table map[string]string
76-
}
77-
78-
func (r *mockResolver) Resolve(name string) (*snet.SCIONAddress, error) {
79-
address, ok := r.table[name]
80-
if !ok {
81-
return nil, &appnet.HostNotFoundError{Host: name}
82-
} else {
83-
a, err := snet.ParseUDPAddr(address)
84-
if err != nil {
85-
panic(fmt.Sprintf("test input must parse %s", err))
86-
}
87-
return &snet.SCIONAddress{IA: a.IA, Host: addr.HostFromIP(a.Host.IP)}, nil
88-
}
89-
}
90-
9177
func TestRoundTripper(t *testing.T) {
92-
93-
resolver := &mockResolver{map[string]string{"host": "1-ff00:0:1,[192.0.2.1]"}}
94-
9578
testCases := []struct {
9679
HostPort string
9780
Expected string
@@ -106,22 +89,37 @@ func TestRoundTripper(t *testing.T) {
10689
}
10790

10891
urlPatterns := hostURLPatterns()
92+
errJustATest := errors.New("just a test")
10993

11094
// We replace the actual dial function of the roundtripper with this function that only
11195
// checks wether the address can be successfully unmangled and resolved.
11296
// expected will be set in the test loop, below
11397
var expected string
11498
testDial := func(ctx context.Context, network, addr string) (net.Conn, error) {
115-
unmangled := appnet.UnmangleSCIONAddr(addr)
116-
resolvedAddr, err := appnet.ResolveUDPAddrAt(unmangled, resolver)
117-
if err != nil {
118-
t.Fatalf("unexpected error when resolving address '%s' in roundtripper: %s", unmangled, err)
99+
// The actual Dialer.DialContext does
100+
// remote, err := pan.ResolveUDPAddr(pan.UnmangleSCIONAddr(addr))
101+
// We mock pan.ResolveUDPAddr here; don't want to rely on hosts files etc
102+
// for this test.
103+
unmangled := pan.UnmangleSCIONAddr(addr)
104+
if strings.HasPrefix(unmangled, "host") {
105+
_, err := pan.ParseUDPAddr(unmangled)
106+
require.Error(t, err)
107+
hostStr, portStr, err := net.SplitHostPort(unmangled)
108+
require.NoError(t, err)
109+
port, err := strconv.Atoi(portStr)
110+
require.NoError(t, err)
111+
require.Equal(t, hostStr, "host")
112+
hostIA, err := pan.ParseIA("1-ff00:0:1")
113+
require.NoError(t, err)
114+
hostIP := netaddr.MustParseIP("192.0.2.1")
115+
remote := pan.UDPAddr{IA: hostIA, IP: hostIP, Port: uint16(port)}
116+
assert.Equal(t, expected, remote.String())
117+
} else {
118+
remote, err := pan.ParseUDPAddr(unmangled)
119+
require.NoError(t, err)
120+
assert.Equal(t, expected, remote.String())
119121
}
120-
actual := resolvedAddr.String()
121-
if actual != expected {
122-
t.Fatalf("unexpected address resolved in roundtripper, actual='%s', expected='%s'", actual, expected)
123-
}
124-
return nil, errors.New("just a test")
122+
return nil, errJustATest
125123
}
126124

127125
c := &http.Client{
@@ -136,11 +134,7 @@ func TestRoundTripper(t *testing.T) {
136134

137135
url := fmt.Sprintf(urlPattern, tc.HostPort)
138136
_, err := c.Get(MangleSCIONAddrURL(url))
139-
if err == nil {
140-
panic("unexpected success!")
141-
} else if !strings.Contains(err.Error(), "just a test") {
142-
t.Fatalf("unexpected error: %s", err)
143-
}
137+
assert.ErrorIs(t, err, errJustATest)
144138
}
145139
}
146140
}

pkg/shttp3/server.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
package shttp3
1616

1717
import (
18+
"context"
1819
"crypto/tls"
1920
"net"
2021
"net/http"
2122

2223
"github.com/lucas-clemente/quic-go/http3"
23-
"github.com/netsec-ethz/scion-apps/pkg/appnet"
24+
25+
"github.com/netsec-ethz/scion-apps/pkg/pan"
2426
)
2527

2628
// Server wraps a http3.Server making it work with SCION
@@ -55,11 +57,11 @@ func ListenAndServe(addr string, certFile, keyFile string, handler http.Handler)
5557
// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to
5658
// handle HTTP/3 requests on incoming connections.
5759
func (s *Server) ListenAndServe() error {
58-
laddr, err := net.ResolveUDPAddr("udp", s.Addr)
60+
laddr, err := pan.ParseOptionalIPPort(s.Addr)
5961
if err != nil {
6062
return err
6163
}
62-
sconn, err := appnet.Listen(laddr)
64+
sconn, err := pan.ListenUDP(context.Background(), laddr, nil)
6365
if err != nil {
6466
return err
6567
}

pkg/shttp3/transport.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,51 @@
1717
package shttp3
1818

1919
import (
20+
"context"
2021
"crypto/tls"
2122

2223
"github.com/lucas-clemente/quic-go"
2324
"github.com/lucas-clemente/quic-go/http3"
24-
"github.com/netsec-ethz/scion-apps/pkg/appnet"
25-
"github.com/netsec-ethz/scion-apps/pkg/appnet/appquic"
25+
"inet.af/netaddr"
26+
27+
"github.com/netsec-ethz/scion-apps/pkg/pan"
2628
)
2729

2830
// DefaultTransport is the default RoundTripper that can be used for HTTP/3
2931
// over SCION.
3032
var DefaultTransport = &http3.RoundTripper{
31-
Dial: Dial,
33+
Dial: (&Dialer{
34+
Policy: nil,
35+
}).Dial,
36+
}
37+
38+
// Dialer dials a QUIC connection over SCION.
39+
// This is the Dialer used for shttp3.DefaultTransport.
40+
type Dialer struct {
41+
Local netaddr.IPPort
42+
Policy pan.Policy
43+
sessions []*pan.QUICEarlySession
44+
}
45+
46+
// Dial dials a QUIC connection over SCION.
47+
func (d *Dialer) Dial(network, addr string, tlsCfg *tls.Config,
48+
cfg *quic.Config) (quic.EarlySession, error) {
49+
50+
remote, err := pan.ResolveUDPAddr(pan.UnmangleSCIONAddr(addr))
51+
if err != nil {
52+
return nil, err
53+
}
54+
session, err := pan.DialQUICEarly(context.TODO(), d.Local, remote, d.Policy, nil, addr, tlsCfg, cfg)
55+
if err != nil {
56+
return nil, err
57+
}
58+
d.sessions = append(d.sessions, session)
59+
return session, nil
3260
}
3361

34-
// Dial is the Dial function used in RoundTripper
35-
func Dial(network, address string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlySession, error) {
36-
return appquic.DialEarly(appnet.UnmangleSCIONAddr(address), tlsCfg, cfg)
62+
func (d *Dialer) SetPolicy(policy pan.Policy) {
63+
d.Policy = policy
64+
for _, s := range d.sessions {
65+
s.Conn.SetPolicy(policy)
66+
}
3767
}

0 commit comments

Comments
 (0)