package main // Copyright 2019 Serge Bazanski // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. import ( "crypto/rand" "encoding/json" "flag" "io" "io/ioutil" "net" "net/http" "strconv" "strings" "github.com/golang/glog" "code.hackerspace.pl/hscloud/bgpwtf/speedtest/static" ) var ( flagBind string flagUseForwardedFor bool ) func cors(w http.ResponseWriter) { w.Header().Add("Access-Control-Allow-Origin", "*") w.Header().Add("Access-Control-Allow-Methods", "GET, POST") } func noCache(w http.ResponseWriter) { w.Header().Add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, s-maxage=0") w.Header().Add("Cache-Control", " post-check=0, pre-check=0") w.Header().Add("Pragma", "no-cache") } func remoteIP(r *http.Request) net.IP { remote := strings.TrimSpace(r.RemoteAddr) if flagUseForwardedFor { remote = strings.TrimSpace(r.Header.Get("X-Forwarded-For")) } if remote == "" { return nil } if strings.HasPrefix(remote, "[") { // Looks like a [::1] v6 address inner := strings.Split(strings.Split(remote, "[")[1], "]")[0] return net.ParseIP(inner) } if strings.Count(remote, ":") == 1 { // Looks like a :4321 port suffix inner := strings.Split(remote, ":")[0] return net.ParseIP(inner) } return net.ParseIP(remote) } func init() { flag.Set("logtostderr", "true") } func main() { flag.StringVar(&flagBind, "bind", "0.0.0.0:8080", "Address at which to serve HTTP requests") flag.BoolVar(&flagUseForwardedFor, "use_forwarded_for", false, "Honor X-Forwarded-For headers to detect user IP") flag.Parse() http.HandleFunc("/empty", func(w http.ResponseWriter, r *http.Request) { cors(w) noCache(w) w.Header().Add("Connection", "keep-alive") w.WriteHeader(http.StatusOK) io.Copy(ioutil.Discard, r.Body) }) http.HandleFunc("/garbage", func(w http.ResponseWriter, r *http.Request) { cors(w) w.Header().Add("Content-Description", "File Tranfer") w.Header().Add("Content-Type", "application/octet-stream") w.Header().Add("Content-Disposition", "attachment; filename=random.dat") w.Header().Add("Content-Transfer-Encoding", "binary") noCache(w) w.WriteHeader(http.StatusOK) chunks := 4 if val, err := strconv.Atoi(r.Header.Get("ckSize")); err == nil { if val > 0 && val <= 1024 { chunks = val } } chunk := make([]byte, 1024*1024) rand.Read(chunk) for i := 0; i < chunks; i += 1 { w.Write(chunk) } }) http.HandleFunc("/ip", func(w http.ResponseWriter, r *http.Request) { cors(w) noCache(w) res := struct { ProcessedString string `json:processedString` RawISPInfo string `json:rawIspInfo` }{ ProcessedString: "", RawISPInfo: "", } ip := remoteIP(r) if ip != nil { res.ProcessedString = ip.String() } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(res) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write(static.Data["bgpwtf/speedtest/index.html"]) }) http.HandleFunc("/speedtest.js", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/javascript") w.Write(static.Data["bgpwtf/speedtest/speedtest.js"]) }) http.HandleFunc("/speedtest_worker.js", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/javascript") w.Write(static.Data["bgpwtf/speedtest/speedtest_worker.js"]) }) glog.Infof("Starting up at %v", flagBind) err := http.ListenAndServe(flagBind, nil) if err != nil { glog.Exit(err) } }