mirror of
https://github.com/jimeh/cloudflare-dyndns.git
synced 2026-02-19 10:56:42 +00:00
Initial commit
This commit is contained in:
173
updater/updater.go
Normal file
173
updater/updater.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cloudflare-go"
|
||||
)
|
||||
|
||||
// DefaultIPCheckURL is the default URL used to figure out the public IP.
|
||||
const DefaultIPCheckURL = "http://whatismyip.akamai.com/"
|
||||
|
||||
// DefaultInterval is the default number of seconds to wait before each update.
|
||||
const DefaultInterval = 30
|
||||
|
||||
// New creates a new DynDNS instance.
|
||||
func New(email string, apiKey string) *Updater {
|
||||
api, err := cloudflare.New(apiKey, email)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return &Updater{
|
||||
API: api,
|
||||
IPCheckURL: DefaultIPCheckURL,
|
||||
Interval: DefaultInterval,
|
||||
}
|
||||
}
|
||||
|
||||
// Updater deals with updating the IP address for a DNS record
|
||||
type Updater struct {
|
||||
API *cloudflare.API
|
||||
IPCheckURL string
|
||||
Interval int
|
||||
}
|
||||
|
||||
// Update performs a the full update sequence.
|
||||
func (u *Updater) Update(host string) error {
|
||||
fmt.Printf("Looking up record for %s...\n", host)
|
||||
record, err := u.RecordByHost(host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Found %s (%s)\n", record.Name, record.ID)
|
||||
|
||||
fmt.Printf("Starting IP check (repeats every %d seconds)\n", u.Interval)
|
||||
for {
|
||||
record, err = u.UpdateRecord(record)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: %s\n", err.Error())
|
||||
fmt.Printf("Retrying in %d seconds...", u.Interval)
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(u.Interval) * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateRecord updates a cloudflare.DNSRecord.
|
||||
func (u *Updater) UpdateRecord(record *cloudflare.DNSRecord) (*cloudflare.DNSRecord, error) {
|
||||
currentIP, err := u.WhatIsMyIP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record, err = u.Record(record.ZoneID, record.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if currentIP != record.Content {
|
||||
fmt.Printf(
|
||||
"Updating %s to %s (was %s)\n",
|
||||
record.Name, currentIP, record.Content,
|
||||
)
|
||||
record.Content = currentIP
|
||||
err = u.API.UpdateDNSRecord(record.ZoneID, record.ID, *record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// Record fetches a cloudflare.DNSRecord from the given Zone and Record IDs.
|
||||
func (u *Updater) Record(zoneID string, recordID string) (*cloudflare.DNSRecord, error) {
|
||||
record, err := u.API.DNSRecord(zoneID, recordID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
// RecordByHost fetches a cloudflare.DNSRecord from the host given.
|
||||
func (u *Updater) RecordByHost(host string) (*cloudflare.DNSRecord, error) {
|
||||
zoneID, err := u.ZoneID(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recordID, err := u.RecordID(host, zoneID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record, err := u.Record(zoneID, recordID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// WhatIsMyIP fetches the public IP via http://whatismyip.akamai.com/
|
||||
func (u *Updater) WhatIsMyIP() (string, error) {
|
||||
client := &http.Client{Timeout: time.Second * 10}
|
||||
resp, err := client.Get(u.IPCheckURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf(
|
||||
"Got a %d response from %s",
|
||||
resp.StatusCode, u.IPCheckURL,
|
||||
)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
// ZoneID finds the zone ID for the relevant Host.
|
||||
func (u *Updater) ZoneID(host string) (string, error) {
|
||||
zones, err := u.API.ListZones()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, zone := range zones {
|
||||
if strings.HasSuffix(host, zone.Name) {
|
||||
return zone.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("No zone found for \"%s\"", host)
|
||||
}
|
||||
|
||||
// RecordID finds the host's DNS record ID.
|
||||
func (u *Updater) RecordID(host string, zoneID string) (string, error) {
|
||||
records, err := u.API.DNSRecords(zoneID, cloudflare.DNSRecord{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, r := range records {
|
||||
if r.Type == "A" && r.Name == host {
|
||||
return r.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("No A type record found for \"%s\"", host)
|
||||
}
|
||||
Reference in New Issue
Block a user