added gandi support

This commit is contained in:
puffaboo 2022-08-01 00:19:30 +01:00
parent daf7f88148
commit 7fd7334ee7
5 changed files with 163 additions and 33 deletions

64
gandi.go Normal file
View File

@ -0,0 +1,64 @@
package main
import (
"fmt"
"github.com/go-gandi/go-gandi"
"github.com/go-gandi/go-gandi/config"
"github.com/go-gandi/go-gandi/livedns"
)
var (
gandiEmailRecords = []livedns.DomainRecord{
{
RrsetType: "MX",
RrsetTTL: 10800,
RrsetName: "@",
RrsetValues: []string{"10 spool.mail.gandi.net.", "50 fb.mail.gandi.net."},
},
{
RrsetType: "TXT",
RrsetTTL: 10800,
RrsetName: "@",
RrsetValues: []string{`"v=spf1 include:_mailcust.gandi.net ?all"`},
},
}
)
type Gandi struct{}
func (g Gandi) IPSet(reqs []IPSetRequest) error {
if len(reqs) == 0 {
return nil
}
if err := g.EnsureAllDomainsAreSame(reqs); err != nil {
return err
}
pwd := reqs[0].Password
domain := reqs[0].Domain
records := make([]livedns.DomainRecord, len(reqs))
for index, req := range reqs {
records[index] = livedns.DomainRecord{
RrsetType: "A",
RrsetTTL: 300,
RrsetName: req.Host,
RrsetValues: []string{req.IP},
}
}
records = append(records, gandiEmailRecords...)
_, err := gandi.NewLiveDNSClient(config.Config{
APIKey: pwd,
}).UpdateDomainRecords(domain, records)
return err
}
func (Gandi) EnsureAllDomainsAreSame(reqs []IPSetRequest) error {
first := reqs[0].Domain
for _, dom := range reqs {
if first != dom.Domain {
return fmt.Errorf("domain mismatch: %s != %s", first, dom.Domain)
}
}
return nil
}

6
go.mod
View File

@ -1,3 +1,9 @@
module ncddns
go 1.18
require (
github.com/go-gandi/go-gandi v0.4.0 // indirect
github.com/peterhellberg/link v1.1.0 // indirect
moul.io/http2curl v1.0.0 // indirect
)

6
go.sum Normal file
View File

@ -0,0 +1,6 @@
github.com/go-gandi/go-gandi v0.4.0 h1:iFVD+x3nJrI611fYvVMk6TKOlUOND/bKnvdQxuZKPSI=
github.com/go-gandi/go-gandi v0.4.0/go.mod h1:9NoYyfWCjFosClPiWjkbbRK5UViaZ4ctpT8/pKSSFlw=
github.com/peterhellberg/link v1.1.0 h1:s2+RH8EGuI/mI4QwrWGSYQCRz7uNgip9BaM04HKu5kc=
github.com/peterhellberg/link v1.1.0/go.mod h1:gtSlOT4jmkY8P47hbTc8PTgiDDWpdPbFYl75keYyBB8=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=

76
main.go
View File

@ -6,7 +6,6 @@ import (
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
)
@ -15,9 +14,28 @@ const (
baseURL = "https://dynamicdns.park-your-domain.com/update?"
)
var (
byName = map[string]IPSetter{
"namecheap": NameCheap{},
"gandi": Gandi{},
}
)
type IPSetRequest struct {
IP string
Domain string
Password string
Host string
}
type IPSetter interface {
IPSet([]IPSetRequest) error
}
type DomainTuple struct {
Domain string
Password string
Setter IPSetter
}
func Config(path string) ([]DomainTuple, error) {
@ -28,16 +46,21 @@ func Config(path string) ([]DomainTuple, error) {
tuples := []DomainTuple{}
for _, line := range strings.Split(string(conf), "\n") {
line := strings.TrimSpace(line)
if !strings.Contains(line, "=") {
if !strings.Contains(line, ",") {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
parts := strings.SplitN(line, ",", 3)
if len(parts) != 3 {
continue
}
setter, ok := byName[parts[0]]
if !ok {
return nil, fmt.Errorf("line [%s] has unsupported provider [%s]", line, parts[0])
}
tuples = append(tuples, DomainTuple{
Domain: parts[0],
Password: parts[1],
Domain: parts[1],
Password: parts[2],
Setter: setter,
})
}
return tuples, nil
@ -60,9 +83,20 @@ func main() {
fatal("config", err)
for _, cfgEntry := range cfg {
fmt.Println(cfgEntry.Domain)
err = Set(ip, cfgEntry.Domain, cfgEntry.Password, "*")
fatal(cfgEntry.Domain, err)
err = Set(ip, cfgEntry.Domain, cfgEntry.Password, "@")
err = cfgEntry.Setter.IPSet([]IPSetRequest{
{
IP: ip,
Domain: cfgEntry.Domain,
Password: cfgEntry.Password,
Host: "*",
},
{
IP: ip,
Domain: cfgEntry.Domain,
Password: cfgEntry.Password,
Host: "@",
},
})
fatal(cfgEntry.Domain, err)
}
}
@ -81,27 +115,3 @@ func IP() (string, error) {
return string(body), nil
}
func Set(ip, domain, password, host string) error {
params := url.Values{}
params.Add("host", host)
params.Add("domain", domain)
params.Add("password", password)
params.Add("ip", ip)
resp, err := http.Get(fmt.Sprintf("%s%s", baseURL, params.Encode()))
if err != nil {
return fmt.Errorf("request: %w", err)
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("read body: %w", err)
}
if resp.StatusCode != http.StatusOK {
fmt.Fprintf(os.Stderr, "got status [%d]: %s\n", resp.StatusCode, string(data))
}
return nil
}

44
namecheap.go Normal file
View File

@ -0,0 +1,44 @@
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
)
type NameCheap struct{}
func (nc NameCheap) IPSet(reqs []IPSetRequest) error {
for _, req := range reqs {
if err := nc.SetSingle(req); err != nil {
return fmt.Errorf("%s: %w", req.Domain, err)
}
}
return nil
}
func (NameCheap) SetSingle(req IPSetRequest) error {
params := url.Values{}
params.Add("host", req.Host)
params.Add("domain", req.Domain)
params.Add("password", req.Password)
params.Add("ip", req.IP)
resp, err := http.Get(fmt.Sprintf("%s%s", baseURL, params.Encode()))
if err != nil {
return fmt.Errorf("request: %w", err)
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("read body: %w", err)
}
if resp.StatusCode != http.StatusOK {
fmt.Fprintf(os.Stderr, "got status [%d]: %s\n", resp.StatusCode, string(data))
}
return nil
}