@@ -28,10 +28,12 @@ import (
28
28
"log"
29
29
"net"
30
30
"net/http"
31
+ "net/url"
31
32
"os"
32
33
"regexp"
33
34
"strings"
34
35
"text/template"
36
+ "time"
35
37
36
38
"github.com/gorilla/handlers"
37
39
"gopkg.in/alecthomas/kingpin.v2"
@@ -80,6 +82,9 @@ func main() {
80
82
apiMux := http .NewServeMux ()
81
83
apiMux .HandleFunc ("/skip.pac" , handleWPAD )
82
84
apiMux .HandleFunc ("/scionHosts" , handleHostListRequest )
85
+ apiMux .HandleFunc ("/r" , handleRedirectBackOrError )
86
+
87
+ apiMux .HandleFunc ("/resolve" , handleHostResolutionRequest )
83
88
apiMux .Handle ("/setPolicy" , policyHandler )
84
89
85
90
mux .Handle ("localhost/" , apiMux )
@@ -126,6 +131,88 @@ func handleHostListRequest(w http.ResponseWriter, req *http.Request) {
126
131
_ , _ = w .Write (buf .Bytes ())
127
132
}
128
133
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
+
129
216
type policyHandler struct {
130
217
output interface { SetPolicy (pan.Policy ) }
131
218
}
@@ -201,11 +288,20 @@ type tunnelHandler struct {
201
288
}
202
289
203
290
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
+ }
205
302
if err != nil {
206
303
fmt .Println ("verbose: " , err .Error ())
207
304
http .Error (w , err .Error (), http .StatusServiceUnavailable )
208
-
209
305
return
210
306
}
211
307
w .WriteHeader (http .StatusOK )
@@ -225,6 +321,7 @@ func (h *tunnelHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
225
321
}
226
322
go transfer (destConn , clientConn )
227
323
go transfer (clientConn , destConn )
324
+
228
325
}
229
326
230
327
func transfer (dst io.WriteCloser , src io.ReadCloser ) {
0 commit comments