Skip to content
Snippets Groups Projects
Unverified Commit 262dbdc6 authored by Philipp Winter's avatar Philipp Winter
Browse files

Initial import of source code.

parent 37254249
No related branches found
No related tags found
No related merge requests found
package main
import (
"fmt"
"golang.org/x/time/rate"
"net"
"net/http"
"time"
)
// limiter implements a rate limiter. We allow 1 request per second on average
// with bursts of up to 5 requests per second.
var limiter = rate.NewLimiter(1, 5)
// Index returns the landing page (which contains the scanning form) to the
// user.
func Index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, IndexPage)
}
// SendResponse sends the given response to the user.
func SendResponse(w http.ResponseWriter, response string) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, response)
}
// ScanDestination extracts the given IP address and port from the POST
// parameters, triggers TCP scanning of the tuple, and returns the result.
func ScanDestination(w http.ResponseWriter, r *http.Request) {
// We implement rate limiting to prevent someone from abusing this service
// as a port scanner.
if limiter.Allow() == false {
SendResponse(w, "Rate limit exceeded. Wait for a bit.")
return
}
// The number of seconds we're willing to wait until we decide that the
// given destination is offline.
timeout, _ := time.ParseDuration("3s")
r.ParseForm()
// These variables will be "" if they're not set.
address := r.Form.Get("address")
port := r.Form.Get("port")
if address == "" {
SendResponse(w, "No address given.")
return
}
if port == "" {
SendResponse(w, "No port given.")
return
}
portReachable, err := IsTCPPortReachable(address, port, timeout)
if portReachable {
SendResponse(w, SuccessPage)
} else {
SendResponse(w, FailurePage(err))
}
}
// IsTCPPortReachable returns `true' if it can establish a TCP connection with
// the given IP address and port. If not, it returns `false' and the
// respective error, as reported by `net.DialTimeout'.
func IsTCPPortReachable(addr, port string, timeout time.Duration) (bool, error) {
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", addr, port), timeout)
if err != nil {
return false, err
}
conn.Close()
return true, nil
}
html.go 0 → 100644
package main
import (
"fmt"
)
// IndexPage contains the landing page of the service. It implements a simple
// form that asks for an IP address and TCP port.
var IndexPage = `<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Test your obfs4 bridge's TCP port</title>
</head>
<body>
<form method="POST" action="scan">
<h2>Tor bridge reachability test</h2>
<p>This service allows you to test if your obfs4 bridge is reachable to the
rest of the world.</p>
<p>Enter your bridge's IP address and obfs4 port, and click 'Scan'. The service
will then try to establish a TCP connection with your bridge and tell you
if it succeeded.</p>
<input type="text" required name="address" placeholder="IP address">
<input type="text" required name="port" placeholder="Obfs4 port">
<label ></label>
<button type="submit">Scan</button>
</form>
</body>
</html>
`
// SuccessPage is shown when the given address and port are reachable.
var SuccessPage = `<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Success!</title>
</head>
<body>
<div align='center'>
<h2 style='color:green'>TCP port is reachable!</h2>
</div>
</body>
</html>
`
// FailurePage2 is shown when the given address and port are unreachable.
func FailurePage(reason error) string {
var failurePage1 = `<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Failure!</title>
</head>
<body>
<div align='center'>
<h2 style='color:red'>TCP port is <i>not</i> reachable!</h2>
<tt>
`
var failurePage2 = `</tt>
</div>
</body>
</html>
`
return fmt.Sprintf("%s%s%s", failurePage1, reason, failurePage2)
}
main.go 0 → 100644
package main
import (
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/gorilla/mux"
)
type Route struct {
Name string
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
type Routes []Route
var routes = Routes{
Route{
"Index",
"GET",
"/",
Index,
},
Route{
"ScanDestination",
"POST",
"/scan",
ScanDestination,
},
}
// NewRouter creates and returns a new request router.
func NewRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
var handler http.Handler
handler = route.HandlerFunc
handler = Logger(handler, route.Name)
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(handler)
}
return router
}
// Logger logs when we receive requests, and the execution time of handling
// these requests. We don't log client IP addresses or the given obfs4
// parameters.
func Logger(inner http.Handler, name string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner.ServeHTTP(w, r)
log.Printf(
"%s\t%s\t%s\t%s",
r.Method,
r.RequestURI,
name,
time.Since(start),
)
})
}
// main is the entry point of this tool.
func main() {
if len(os.Args) != 3 {
fmt.Printf("Usage: %s CERT_FILE KEY_FILE\n", os.Args[0])
os.Exit(1)
}
certFile := os.Args[1]
keyFile := os.Args[2]
router := NewRouter()
log.Fatal(http.ListenAndServeTLS(":8080", certFile, keyFile, router))
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment