package main // import "git.makerforce.io/cacti/he-dns-editor" import ( "errors" "flag" "io" "log" "net/http" "strings" "github.com/julienschmidt/httprouter" "golang.org/x/net/html" ) var listen string var username string var password string var zoneid string func init() { flag.StringVar(&listen, "listen", ":8080", "Listen on port") flag.StringVar(&username, "username", "", "HE.net username") flag.StringVar(&password, "password", "", "HE.net password") flag.StringVar(&zoneid, "zoneid", "", "HE.net zone ID") } func main() { flag.Parse() if len(username) == 0 || len(password) == 0 || len(zoneid) == 0 { flag.PrintDefaults() log.Fatal("Please specify username, password and zoneid") } router := httprouter.New() router.GET("/", Index) router.POST("/", Post) // Listen log.Println("main: Listening on", listen) log.Fatal(http.ListenAndServe(listen, router)) } func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { w.Header().Add("Content-Type", "text/html") records, err := request("", "", "", "", "") if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("An error occurred")) log.Println(err) return } res := `
` for _, record := range records { disabled := "" if record.Type == "NS" || record.Type == "SOA" { disabled = ` disabled` } res += `
` res += `` res += `` res += `` res += `` res += `` res += `` res += `
` } res += `
` res += `` res += `` res += `` res += `` res += `
` res += `
` w.Write([]byte(res)) } func Post(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { _, err := request( r.FormValue("editrecord"), r.FormValue("recordid"), r.FormValue("recordname"), r.FormValue("recordtype"), r.FormValue("recordcontent"), ) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("An error occurred")) log.Println(err) return } w.Header().Add("Location", "/") w.WriteHeader(http.StatusSeeOther) } var BadResponseError = errors.New("Bad response") type Record struct { ID string Name string Type string Content string } func request(editrecord, recordid, recordname, recordtype, recordcontent string) ([]Record, error) { log.Println("Operation", editrecord, recordid, recordname, recordtype, recordcontent) body := "email=" + username + "&pass=" + password + "&account=" body += "&menu=edit_zone&hosted_dns_editzone=1&hosted_dns_zoneid=" + zoneid if len(editrecord) > 0 { if editrecord == "Delete" { body += "&hosted_dns_delconfirm=DELETE&hosted_dns_delrecord=1" } else { body += "&hosted_dns_editrecord=" + editrecord } } if len(recordid) > 0 { body += "&hosted_dns_recordid=" + recordid } if len(recordname) > 0 { body += "&Name=" + recordname } if len(recordtype) > 0 { body += "&Type=" + recordtype } body += "&TTL=300" if len(recordcontent) > 0 { body += "&Content=" + recordcontent } buf := strings.NewReader(body) resp, err := http.Post("https://dns.he.net", "application/x-www-form-urlencoded", buf) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, BadResponseError } return parseRequest(resp) } func parseRequest(resp *http.Response) ([]Record, error) { d := html.NewTokenizer(resp.Body) records := make([]Record, 0) working := Record{} tdCount := 0 var err error for err == nil { tok := d.Next() switch tok { case html.ErrorToken: err = d.Err() case html.TextToken: text := string(d.Text()) switch tdCount { case 2: working.Name = text case 3: working.Type = text case 6: working.Content = text } case html.StartTagToken: tagName, _ := d.TagName() tagAttr := make(map[string]string) var k, v []byte more := true for more { k, v, more = d.TagAttr() tagAttr[string(k)] = string(v) } if string(tagName) == "tr" { if tagAttr["class"] == "dns_tr" || tagAttr["class"] == "dns_tr_dynamic" { tdCount = 0 working = Record{} } if _, ok := tagAttr["id"]; ok { working.ID = tagAttr["id"] } } case html.EndTagToken: tagName, _ := d.TagName() if string(tagName) == "tr" { if len(working.ID) == 0 { break } records = append(records, working) } else if string(tagName) == "td" { tdCount += 1 } } } if err == io.EOF { err = nil } return records, err }