Skip to content

Commit 8ebcb0c

Browse files
Extend Skip functionality for latest scion-browser extension (#233)
Adds some functionality to the skip proxy (e.g. support for TCP/SCION proxying, hostname resolution, etc) that was implemented in the chrome/brave browser extension for the hotnets 2022 paper. * Add /resolve endpoint * Add /r endpoint, redirect-back-or-error The proxy side of the redirect-crazyness required to work around limitations of chromium APIs. * Handle TCP and SCION tunneling Co-authored-by: Jordi Subirà Nieto <[email protected]>
1 parent 5faf843 commit 8ebcb0c

File tree

1 file changed

+99
-2
lines changed

1 file changed

+99
-2
lines changed

skip/main.go

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ import (
2828
"log"
2929
"net"
3030
"net/http"
31+
"net/url"
3132
"os"
3233
"regexp"
3334
"strings"
3435
"text/template"
36+
"time"
3537

3638
"github.com/gorilla/handlers"
3739
"gopkg.in/alecthomas/kingpin.v2"
@@ -80,6 +82,9 @@ func main() {
8082
apiMux := http.NewServeMux()
8183
apiMux.HandleFunc("/skip.pac", handleWPAD)
8284
apiMux.HandleFunc("/scionHosts", handleHostListRequest)
85+
apiMux.HandleFunc("/r", handleRedirectBackOrError)
86+
87+
apiMux.HandleFunc("/resolve", handleHostResolutionRequest)
8388
apiMux.Handle("/setPolicy", policyHandler)
8489

8590
mux.Handle("localhost/", apiMux)
@@ -126,6 +131,88 @@ func handleHostListRequest(w http.ResponseWriter, req *http.Request) {
126131
_, _ = w.Write(buf.Bytes())
127132
}
128133

134+
func handleRedirectBackOrError(w http.ResponseWriter, req *http.Request) {
135+
136+
if req.Method != http.MethodGet {
137+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
138+
return
139+
}
140+
141+
// We need this here, it's required for redirecting properly
142+
// We may set localhost here but this would stop us from
143+
// running one skip for multiple clients later...
144+
w.Header().Set("Access-Control-Allow-Origin", "*")
145+
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
146+
q := req.URL.Query()
147+
148+
urls, ok := q["url"]
149+
if !ok || len(urls) != 1 {
150+
http.Error(w, "Bad request", http.StatusBadRequest)
151+
return
152+
}
153+
url, err := url.Parse(urls[0])
154+
if err != nil {
155+
fmt.Println(err)
156+
http.Error(w, "Bad request", http.StatusBadRequest)
157+
return
158+
}
159+
160+
hostPort := url.Host + ":0"
161+
162+
w.Header().Set("Location", url.String())
163+
_, err = pan.ResolveUDPAddr(req.Context(), hostPort)
164+
if err != nil {
165+
fmt.Println("verbose: ", err.Error())
166+
http.Error(w, "Internal error", http.StatusInternalServerError)
167+
return
168+
}
169+
170+
http.Redirect(w, req, url.String(), http.StatusMovedPermanently)
171+
}
172+
173+
// handleHostResolutionRequest parses requests in the form: /resolve?host=XXX
174+
// If the PAN lib cannot resolve the host, it sends back an empty response.
175+
func handleHostResolutionRequest(w http.ResponseWriter, req *http.Request) {
176+
if req.Method != http.MethodGet {
177+
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
178+
return
179+
}
180+
buf := &bytes.Buffer{}
181+
q := req.URL.Query()
182+
hosts, ok := q["host"]
183+
if !ok || len(hosts) > 1 {
184+
http.Error(w, "Bad request", http.StatusBadRequest)
185+
return
186+
}
187+
hostPort := hosts[0] + ":0"
188+
189+
res, err := pan.ResolveUDPAddr(req.Context(), hostPort)
190+
if err != nil {
191+
fmt.Println("verbose: ", err.Error())
192+
ok := errors.As(err, &pan.HostNotFoundError{})
193+
if !ok {
194+
http.Error(w, "Internal error", http.StatusInternalServerError)
195+
}
196+
return
197+
}
198+
buf.WriteString(strings.TrimRight(res.String(), ":0"))
199+
w.WriteHeader(http.StatusOK)
200+
_, _ = w.Write(buf.Bytes())
201+
}
202+
203+
func isSCIONEnabled(ctx context.Context, host string) (bool, error) {
204+
_, err := pan.ResolveUDPAddr(ctx, host)
205+
if err != nil {
206+
fmt.Println("verbose: ", err.Error())
207+
ok := errors.As(err, &pan.HostNotFoundError{})
208+
if !ok {
209+
return false, err
210+
}
211+
return false, nil
212+
}
213+
return true, nil
214+
}
215+
129216
type policyHandler struct {
130217
output interface{ SetPolicy(pan.Policy) }
131218
}
@@ -201,11 +288,20 @@ type tunnelHandler struct {
201288
}
202289

203290
func (h *tunnelHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
204-
destConn, err := h.dialer.DialContext(context.Background(), "", req.Host)
291+
hostPort := req.Host
292+
var destConn net.Conn
293+
var err error
294+
enabled, _ := isSCIONEnabled(req.Context(), hostPort)
295+
if !enabled {
296+
// CONNECT via TCP/IP
297+
destConn, err = net.DialTimeout("tcp", req.Host, 10*time.Second)
298+
} else {
299+
// CONNECT via SCION
300+
destConn, err = h.dialer.DialContext(context.Background(), "", req.Host)
301+
}
205302
if err != nil {
206303
fmt.Println("verbose: ", err.Error())
207304
http.Error(w, err.Error(), http.StatusServiceUnavailable)
208-
209305
return
210306
}
211307
w.WriteHeader(http.StatusOK)
@@ -225,6 +321,7 @@ func (h *tunnelHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
225321
}
226322
go transfer(destConn, clientConn)
227323
go transfer(clientConn, destConn)
324+
228325
}
229326

230327
func transfer(dst io.WriteCloser, src io.ReadCloser) {

0 commit comments

Comments
 (0)