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 := `
Twistee |
Summary |
{{range .}}
{{.Key}}
|
{{.Value}}
|
{{end}}
`
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")
case statusCG:
fmt.Fprintf(w, "Twistee Status: statusCG")
case statusWG:
fmt.Fprintf(w, "Twistee Status: statusWG")
case statusNG:
fmt.Fprintf(w, "Twistee Status: statusNG")
}
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)
}
}
/*
*/