Skip to content

Commit 3622469

Browse files
authored
Merge branch 'master' into master
2 parents 8aef6cb + b4c6d51 commit 3622469

File tree

9 files changed

+367
-243
lines changed

9 files changed

+367
-243
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()

webapp/lib/bwcont.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,16 @@ func GetBwByTimeHandler(w http.ResponseWriter, r *http.Request, active bool, src
141141
since := r.PostFormValue("since")
142142
log.Info("Requesting data since", "timestamp", since)
143143
// find undisplayed test results
144-
bwTestResults := model.ReadBwTestItemsSince(since)
144+
bwTestResults, err := model.ReadBwTestItemsSince(since)
145+
if CheckError(err) {
146+
returnError(w, err)
147+
return
148+
}
145149
log.Debug("Requested data:", "bwTestResults", bwTestResults)
146150

147151
bwtestsJSON, err := json.Marshal(bwTestResults)
148152
if CheckError(err) {
153+
returnError(w, err)
149154
return
150155
}
151156
jsonBuf := []byte(`{ "graph": ` + string(bwtestsJSON))

webapp/lib/sciond.go

Lines changed: 78 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ func returnError(w http.ResponseWriter, err error) {
3838
fmt.Fprintf(w, `{"err":`+strconv.Quote(err.Error())+`}`)
3939
}
4040

41+
func returnPathHandler(w http.ResponseWriter, pathJson []byte, segJson []byte, err error) {
42+
var buffer bytes.Buffer
43+
buffer.WriteString(`{"src":"sciond"`)
44+
if pathJson != nil {
45+
buffer.WriteString(fmt.Sprintf(`,"paths":%s`, pathJson))
46+
}
47+
if segJson != nil {
48+
buffer.WriteString(fmt.Sprintf(`,"segments":%s`, segJson))
49+
}
50+
if err != nil {
51+
buffer.WriteString(fmt.Sprintf(`,"err":%s`, strconv.Quote(err.Error())))
52+
}
53+
buffer.WriteString(`}`)
54+
fmt.Fprintf(w, buffer.String())
55+
}
56+
4157
// sciond data sources and calls
4258

4359
// PathTopoHandler handles requests for paths, returning results from sciond.
@@ -78,34 +94,55 @@ func PathTopoHandler(w http.ResponseWriter, r *http.Request) {
7894
}
7995
}
8096

81-
paths := getPaths(*clientCCAddr, *serverCCAddr)
82-
if len(paths) == 0 {
83-
returnError(w, fmt.Errorf("No paths from %s to %s", clientCCAddr.IA,
84-
serverCCAddr.IA))
97+
paths, err := getPathsJSON(*clientCCAddr, *serverCCAddr)
98+
if CheckError(err) {
99+
returnError(w, err)
100+
return
101+
}
102+
log.Debug("PathTopoHandler:", "paths", string(paths))
103+
104+
// Since segments data is supplimentary to paths data, if segments data
105+
// fails, provide the error, but we must still allow paths data to return.
106+
segments, err := getSegmentsJSON(*clientCCAddr)
107+
if CheckError(err) {
108+
returnPathHandler(w, paths, nil, err)
85109
return
86110
}
111+
log.Debug("PathTopoHandler:", "segments", string(segments))
87112

88-
jsonPathInfo, _ := json.Marshal(paths)
89-
log.Debug("PathTopoHandler:", "jsonPathInfo", string(jsonPathInfo))
113+
returnPathHandler(w, paths, segments, err)
114+
}
90115

116+
func getSegmentsJSON(local snet.Addr) ([]byte, error) {
91117
// load segments from paths database
92-
var dbSrcFile = findDBFilename(clientCCAddr.IA)
93-
dbTmpFile := copyDBToTemp(dbSrcFile)
118+
var dbSrcFile = findDBFilename(local.IA)
119+
dbTmpFile, err := copyDBToTemp(dbSrcFile)
120+
if err != nil {
121+
return nil, err
122+
}
94123
// since http.ListenAndServe() blocks, ensure we generate a local db object
95124
// which will live only during the http call
96-
db := pathdb.InitDB(dbTmpFile)
125+
db, err := pathdb.InitDB(dbTmpFile)
126+
if err != nil {
127+
return nil, err
128+
}
97129
defer func() {
98130
pathdb.CloseDB(db)
99131
removeAllDir(filepath.Dir(dbTmpFile))
100132
}()
101-
segTypes := pathdb.ReadSegTypesAll(db)
102-
segments := pathdb.ReadSegmentsAll(db, segTypes)
103-
104-
jsonSegsInfo, _ := json.Marshal(segments)
105-
log.Debug("PathTopoHandler:", "jsonSegsInfo", string(jsonSegsInfo))
106-
107-
fmt.Fprintf(w, fmt.Sprintf(`{"paths":%s,"segments":%s}`,
108-
jsonPathInfo, jsonSegsInfo))
133+
segTypes, err := pathdb.ReadSegTypesAll(db)
134+
if err != nil {
135+
return nil, err
136+
}
137+
segments, err := pathdb.ReadSegmentsAll(db, segTypes)
138+
if err != nil {
139+
return nil, err
140+
}
141+
jsonSegsInfo, err := json.Marshal(segments)
142+
if err != nil {
143+
return nil, err
144+
}
145+
return jsonSegsInfo, nil
109146
}
110147

111148
func findDBFilename(ia addr.IA) string {
@@ -120,52 +157,60 @@ func findDBFilename(ia addr.IA) string {
120157
}
121158

122159
// returns the name of the created file
123-
func copyDBToTemp(filename string) string {
160+
func copyDBToTemp(filename string) (string, error) {
124161
copyOneFile := func(dstDir, srcFileName string) error {
125162
src, err := os.Open(srcFileName)
126-
if CheckError(err) {
127-
return fmt.Errorf("Cannot open %s: %v", srcFileName, err)
163+
if err != nil {
164+
return err
128165
}
129166
defer src.Close()
130167
dstFilename := filepath.Join(dstDir, filepath.Base(srcFileName))
131168
dst, err := os.Create(dstFilename)
132-
if CheckError(err) {
133-
return fmt.Errorf("Cannot open %s: %v", dstFilename, err)
169+
if err != nil {
170+
return err
134171
}
135172
defer dst.Close()
136173
_, err = io.Copy(dst, src)
137-
if CheckError(err) {
138-
return fmt.Errorf("Cannot copy %s to %s: %v", srcFileName, dstFilename, err.Error())
174+
if err != nil {
175+
return err
139176
}
140177
return nil
141178
}
142179
dirName, err := ioutil.TempDir("/tmp", "sciond_dump")
143-
if CheckError(err) {
144-
return err.Error()
180+
if err != nil {
181+
return "", err
145182
}
146-
147183
err = copyOneFile(dirName, filename)
148-
if CheckError(err) {
149-
fmt.Fprintf(os.Stderr, "No panic: %v", err)
184+
if err != nil {
185+
return "", err
150186
}
151187
err = copyOneFile(dirName, filename+"-wal")
152-
CheckError(err)
153-
return filepath.Join(dirName, filepath.Base(filename))
188+
if err != nil {
189+
return "", err
190+
}
191+
return filepath.Join(dirName, filepath.Base(filename)), nil
154192
}
155193

156194
func removeAllDir(dirName string) {
157195
err := os.RemoveAll(dirName)
158196
CheckError(err)
159197
}
160198

161-
func getPaths(local snet.Addr, remote snet.Addr) []*spathmeta.AppPath {
199+
func getPathsJSON(local snet.Addr, remote snet.Addr) ([]byte, error) {
162200
pathMgr := snet.DefNetwork.PathResolver()
163201
pathSet := pathMgr.Query(context.Background(), local.IA, remote.IA)
202+
if len(pathSet) == 0 {
203+
return nil, fmt.Errorf("No paths from %s to %s", local.IA, remote.IA)
204+
}
164205
var appPaths []*spathmeta.AppPath
165206
for _, path := range pathSet {
166207
appPaths = append(appPaths, path)
167208
}
168-
return appPaths
209+
jsonPathInfo, err := json.Marshal(appPaths)
210+
if err != nil {
211+
return nil, err
212+
}
213+
return jsonPathInfo, nil
169214
}
170215

171216
// AsTopoHandler handles requests for AS data, returning results from sciond.

0 commit comments

Comments
 (0)