package main import ( "fmt" "html" "log" "net/http" "text/template" "time" ) // startHTTP runs in a goroutine and provides the web interface // to the dnsseeder func startHTTP(port string) { http.HandleFunc("/dns", dnsHandler) http.HandleFunc("/twistee", twisteeHandler) http.HandleFunc("/statusRG", statusRGHandler) http.HandleFunc("/statusCG", statusCGHandler) http.HandleFunc("/statusWG", statusWGHandler) http.HandleFunc("/statusNG", statusNGHandler) http.HandleFunc("/", emptyHandler) // listen only on localhost err := http.ListenAndServe("127.0.0.1:"+port, nil) if err != nil { log.Fatal("ListenAndServe: ", err) } } // reflectHandler processes all requests and returns output in the requested format func dnsHandler(w http.ResponseWriter, r *http.Request) { st := time.Now() // FIXME - This is ugly code and needs to be cleaned up a lot // get v4 std addresses v4std := getv4stdRR() v4non := getv4nonRR() v6std := getv6stdRR() v6non := getv6nonRR() var v4stdstr, v4nonstr []string var v6stdstr, v6nonstr []string if x := len(v4std); x > 0 { v4stdstr = make([]string, x) for k, v := range v4std { v4stdstr[k] = v.String() } } else { v4stdstr = []string{"No records Available"} } if x := len(v4non); x > 0 { v4nonstr = make([]string, x) for k, v := range v4non { v4nonstr[k] = v.String() } } else { v4nonstr = []string{"No records Available"} } // ipv6 if x := len(v6std); x > 0 { v6stdstr = make([]string, x) for k, v := range v6std { v6stdstr[k] = v.String() } } else { v6stdstr = []string{"No records Available"} } if x := len(v6non); x > 0 { v6nonstr = make([]string, x) for k, v := range v6non { v6nonstr[k] = v.String() } } else { v6nonstr = []string{"No records Available"} } t1 := `
Standard Ports Non Standard Ports
` t2 := ` {{range .}} {{.}}
{{end}} ` t3 := `
` t4 := `
` writeHeader(w, r) fmt.Fprintf(w, "Currently serving the following DNS records") fmt.Fprintf(w, "

IPv4

") fmt.Fprintf(w, t1) t := template.New("v4 template") t, err := t.Parse(t2) if err != nil { log.Printf("error parsing template v4 %v\n", err) } err = t.Execute(w, v4stdstr) if err != nil { log.Printf("error executing template v4 %v\n", err) } fmt.Fprintf(w, t3) err = t.Execute(w, v4nonstr) if err != nil { log.Printf("error executing template v4 non %v\n", err) } fmt.Fprintf(w, t4) // ipv6 records fmt.Fprintf(w, "

IPv6

") fmt.Fprintf(w, t1) err = t.Execute(w, v6stdstr) if err != nil { log.Printf("error executing template v6 %v\n", err) } fmt.Fprintf(w, t3) err = t.Execute(w, v6nonstr) if err != nil { log.Printf("error executing template v6 non %v\n", err) } fmt.Fprintf(w, t4) writeFooter(w, r, st) } // emptyHandler processes all requests for non-existant urls func emptyHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") fmt.Fprintf(w, "Nothing to see here. Move along please\n") } func statusRGHandler(w http.ResponseWriter, r *http.Request) { statusHandler(w, r, statusRG) } func statusCGHandler(w http.ResponseWriter, r *http.Request) { statusHandler(w, r, statusCG) } func statusWGHandler(w http.ResponseWriter, r *http.Request) { statusHandler(w, r, statusWG) } func statusNGHandler(w http.ResponseWriter, r *http.Request) { statusHandler(w, r, statusNG) } type webstatus struct { Key string Value string } func statusHandler(w http.ResponseWriter, r *http.Request, status uint32) { startT := time.Now() // gather all the info before writing anything to the remote browser ws := generateWebStatus(status) st := `
{{range .}} {{end}}
Twistee Summary
{{.Key}} {{.Value}}
` writeHeader(w, r) if len(ws) == 0 { fmt.Fprintf(w, "No Twistees found with this status") } else { switch status { case statusRG: fmt.Fprintf(w, "
Twistee Status: statusRG - (Reported Good) Have not been able to get addresses yet
") case statusCG: fmt.Fprintf(w, "
Twistee Status: statusCG - (Currently Good) Able to connect and get addresses
") case statusWG: fmt.Fprintf(w, "
Twistee Status: statusWG - (Was Good) Was Ok but now can not get addresses
") case statusNG: fmt.Fprintf(w, "
Twistee Status: statusNG - (No Good) Unable to get addresses
") } t := template.New("Status template") t, err := t.Parse(st) if err != nil { log.Printf("error parsing status template %v\n", err) } err = t.Execute(w, ws) if err != nil { log.Printf("error executing status template %v\n", err) } } writeFooter(w, r, startT) } // copy Twistee details into a template friendly struct type webtemplate struct { Key string IP string Port uint16 Statusstr string Rating string Dnstype string Lastconnect string Lastconnectago string Lasttry string Lasttryago string Crawlstart string Crawlstartago string Crawlactive bool Connectfails uint32 Version int32 Strversion string Services string Lastblock int32 Nonstdip string } // reflectHandler processes all requests and returns output in the requested format func twisteeHandler(w http.ResponseWriter, r *http.Request) { st := time.Now() twt := `
Twistee {{.Key}}Details
IP Address{{.IP}}
Port{{.Port}}
DNS Type{{.Dnstype}}
Non Standard IP{{.Nonstdip}}
Last Connect{{.Lastconnect}}
{{.Lastconnectago}} ago
Last Connect Status{{.Statusstr}}
Last Try{{.Lasttry}}
{{.Lasttryago}} ago
Crawl Start{{.Crawlstart}}
{{.Crawlstartago}} ago
Crawl Active{{.Crawlactive}}
Connection Fails{{.Connectfails}}
Remote Version{{.Version}}
Remote SubVersion{{.Strversion}}
Remote Services{{.Services}}
Remote Last Block{{.Lastblock}}
` s := config.seeder s.mtx.RLock() defer s.mtx.RUnlock() // skip the tw= from the raw query k := html.UnescapeString(r.URL.RawQuery[3:]) writeHeader(w, r) if _, ok := s.theList[k]; ok == false { fmt.Fprintf(w, "Sorry there is no Twistee with those details\n") } else { tw := s.theList[k] wt := webtemplate{ IP: tw.na.IP.String(), Port: tw.na.Port, Dnstype: tw.dns2str(), Nonstdip: tw.nonstdIP.String(), Statusstr: tw.statusStr, Lastconnect: tw.lastConnect.String(), Lastconnectago: time.Since(tw.lastConnect).String(), Lasttry: tw.lastTry.String(), Lasttryago: time.Since(tw.lastTry).String(), Crawlstart: tw.crawlStart.String(), Crawlstartago: time.Since(tw.crawlStart).String(), Connectfails: tw.connectFails, Crawlactive: tw.crawlActive, Version: tw.version, Strversion: tw.strVersion, Services: tw.services.String(), Lastblock: tw.lastBlock, } // display details for the Twistee t := template.New("Twistee template") t, err := t.Parse(twt) if err != nil { log.Printf("error parsing Twistee template %v\n", err) } err = t.Execute(w, wt) if err != nil { log.Printf("error executing Twistee template %v\n", err) } } writeFooter(w, r, st) } // generateWebStatus is given a twistee status and returns a slice of webstatus structures // ready to be ranged over by an html/template func generateWebStatus(status uint32) (ws []webstatus) { s := config.seeder s.mtx.RLock() defer s.mtx.RUnlock() var valueStr string for k, v := range s.theList { if v.status != status { continue } switch status { case statusRG: valueStr = fmt.Sprintf("Fail Count: %v DNS Type: %s", v.connectFails, v.dns2str()) case statusCG: valueStr = fmt.Sprintf("Remote Version: %v%s Last Block: %v DNS Type: %s", v.version, v.strVersion, v.lastBlock, v.dns2str()) case statusWG: valueStr = fmt.Sprintf("Last Try: %s ago Last Status: %s\n", time.Since(v.lastTry).String(), v.statusStr) case statusNG: valueStr = fmt.Sprintf("Fail Count: %v Last Try: %s ago Last Status: %s\n", v.connectFails, time.Since(v.lastTry).String(), v.statusStr) default: valueStr = "" } ows := webstatus{ Key: k, Value: valueStr, } ws = append(ws, ows) } return ws } // genHeader will output the standard header func writeHeader(w http.ResponseWriter, r *http.Request) { var hc struct { RG uint32 RGS uint32 CG uint32 CGS uint32 WG uint32 WGS uint32 NG uint32 NGS uint32 Total uint32 V4Std uint32 V4Non uint32 V6Std uint32 V6Non uint32 DNSTotal uint32 } // fill the structs so they can be displayed via the template counts.mtx.RLock() hc.RG = counts.TwStatus[statusRG] hc.RGS = counts.TwStarts[statusRG] hc.CG = counts.TwStatus[statusCG] hc.CGS = counts.TwStarts[statusCG] hc.WG = counts.TwStatus[statusWG] hc.WGS = counts.TwStarts[statusWG] hc.NG = counts.TwStatus[statusNG] hc.NGS = counts.TwStarts[statusNG] hc.Total = hc.RG + hc.CG + hc.WG + hc.NG hc.V4Std = counts.DNSCounts[dnsV4Std] hc.V4Non = counts.DNSCounts[dnsV4Non] hc.V6Std = counts.DNSCounts[dnsV6Std] hc.V6Non = counts.DNSCounts[dnsV6Non] hc.DNSTotal = hc.V4Std + hc.V4Non + hc.V6Std + hc.V6Non counts.mtx.RUnlock() // we are using basic and simple html here. No fancy graphics or css h := ` dnsseeder
statusRG statusCG statusWG statusNG DNS
Twistee Stats (count/started)
RG: {{.RG}}/{{.RGS}}CG: {{.CG}}/{{.CGS}}WG: {{.WG}}/{{.WGS}}NG: {{.NG}}/{{.NGS}}Total: {{.Total}}
DNS Requests
V4 Std: {{.V4Std}}V4 Non: {{.V4Non}}V6 Std: {{.V6Std}}V6 Non: {{.V6Non}}Total: {{.DNSTotal}}

` t := template.New("Header template") t, err := t.Parse(h) if err != nil { log.Printf("error parsing template %v\n", err) } err = t.Execute(w, hc) if err != nil { log.Printf("error executing template %v\n", err) } } // genFooter will output the standard footer func writeFooter(w http.ResponseWriter, r *http.Request, st time.Time) { // Footer needs to be exported for template processing to work var Footer struct { Uptime string Version string Rt string } f := `
Version: {{.Version}} Uptime: {{.Uptime}} Request Time: {{.Rt}}
` Footer.Uptime = time.Since(config.seeder.uptime).String() Footer.Version = config.version Footer.Rt = time.Since(st).String() t := template.New("Footer template") t, err := t.Parse(f) if err != nil { log.Printf("error parsing template %v\n", err) } err = t.Execute(w, Footer) if err != nil { log.Printf("error executing template %v\n", err) } if config.verbose { log.Printf("status - processed web request: %s %s\n", r.RemoteAddr, r.RequestURI) } } /* */