1
0
Fork 0
he-dns-editor/main.go

204 lines
5.0 KiB
Go

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 := `
<style>
form {
margin-bottom: 0;
}
</style>
<main>`
for _, record := range records {
disabled := ""
if record.Type == "NS" || record.Type == "SOA" {
disabled = ` disabled`
}
res += `<form method="POST">`
res += `<input type="hidden" name="recordid" value="` + record.ID + `"` + disabled + `>`
res += `<input name="recordname" value="` + record.Name + `"` + disabled + `>`
res += `<input name="recordtype" value="` + record.Type + `"` + disabled + `>`
res += `<input name="recordcontent" value="` + record.Content + `"` + disabled + `>`
res += `<input type="submit" name="editrecord" value="Update"` + disabled + `>`
res += `<input type="submit" name="editrecord" value="Delete"` + disabled + `>`
res += `</form>`
}
res += `<form method="POST">`
res += `<input name="recordname" placeholder="Name">`
res += `<input name="recordtype" placeholder="Type" value="CNAME">`
res += `<input name="recordcontent" placeholder="Data">`
res += `<input type="submit" name="editrecord" value="Submit">`
res += `</form>`
res += `</main>`
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
}