204 lines
5.0 KiB
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
|
|
}
|