-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
150 lines (122 loc) · 3.78 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/go-resty/resty/v2"
"github.com/robfig/cron/v3"
)
var (
authToken string
zoneIdentifier string
recordName string
)
type DNSRecord struct {
ID string `json:"id"`
Content string `json:"content"`
}
type CloudflareResponse struct {
Result []DNSRecord `json:"result"`
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
}
func main() {
flag.StringVar(&authToken, "authToken", "", "Cloudflare API Token")
flag.StringVar(&zoneIdentifier, "zoneIdentifier", "", "Cloudflare Zone Identifier")
flag.StringVar(&recordName, "recordName", "", "DNS Record Name")
flag.Parse()
if authToken == "" || zoneIdentifier == "" || recordName == "" {
fmt.Println("Please provide all required parameters: -authToken, -zoneIdentifier, -recordName")
return
}
err := runUpdate()
if err != nil {
fmt.Printf("First update error: %v\n", err)
}
c := cron.New()
// Run the task every hour
c.AddFunc("@hourly", func() {
err := runUpdate()
if err != nil {
fmt.Printf("Update error: %v\n", err)
}
})
c.Start()
select {}
}
func runUpdate() error {
fmt.Println("Check Initiated")
ip, err := getExternalIP()
if err != nil {
return fmt.Errorf("Network error, cannot fetch external network IP: %v", err)
}
fmt.Printf(" > Fetched current external network IP: %s\n", ip)
headerAuthParamHeader := make(map[string]string)
if authToken != "" {
headerAuthParamHeader["Authorization"] = "Bearer " + authToken
}
seekCurrentDNSValueURL := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records?name=%s&type=A", zoneIdentifier, recordName)
response, err := executeRequest("GET", seekCurrentDNSValueURL, nil, headerAuthParamHeader)
if err != nil {
return fmt.Errorf("Network error, cannot fetch DNS record: %v", err)
}
var cfResponse CloudflareResponse
if err := json.Unmarshal(response, &cfResponse); err != nil {
return fmt.Errorf("Error decoding JSON response: %v", err)
}
if !cfResponse.Success {
fmt.Println("Error in Cloudflare API response.")
return nil
}
// Ensure there is at least one record
if len(cfResponse.Result) == 0 {
fmt.Println("No DNS records found.")
return nil
}
// We'll process the first record from the response
record := cfResponse.Result[0]
fmt.Printf(" > Fetched current DNS record value : %s\n", record.Content)
if ip == record.Content {
fmt.Printf("Update for A record '%s' cancelled.\n Reason: IP has not changed.\n", record.ID)
return nil
}
fmt.Println(" > Different IP addresses detected, synchronizing...")
jsonDataV4 := fmt.Sprintf(`{"id":"%s","type":"A","proxied":false,"name":"%s","content":"%s","ttl":120}`, zoneIdentifier, recordName, ip)
updateURL := fmt.Sprintf("https://api.cloudflare.com/client/v4/zones/%s/dns_records/%s", zoneIdentifier, record.ID)
_, err = executeRequest("PUT", updateURL, []byte(jsonDataV4), headerAuthParamHeader)
if err != nil {
return fmt.Errorf("Update error: %v", err)
}
fmt.Printf("Update for A record '%s' succeeded.\n - Old value: %s\n + New value: %s\n", record.ID, record.Content, ip)
return nil
}
func getExternalIP() (string, error) {
resp, err := http.Get("https://icanhazip.com/")
if err != nil {
return "", err
}
defer resp.Body.Close()
ipBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
ip := strings.TrimSpace(string(ipBytes))
return ip, nil
}
func executeRequest(method, url string, data []byte, headers map[string]string) ([]byte, error) {
client := resty.New()
client.SetTimeout(10 * time.Second)
client.SetHeaders(headers)
response, err := client.R().
SetBody(data).
Execute(method, url)
if err != nil {
return nil, err
}
return response.Body(), nil
}