mirror of
https://github.com/taigrr/godns
synced 2025-01-18 04:03:25 -08:00
commit
015eb5bb71
23
README.md
23
README.md
@ -29,6 +29,7 @@ GoDNS is a dynamic DNS (DDNS) client tool, it is based on my early open source p
|
|||||||
Now I rewrite [DynDNS](https://github.com/TimothyYe/DynDNS) by Golang and call it [GoDNS](https://github.com/TimothyYe/godns).
|
Now I rewrite [DynDNS](https://github.com/TimothyYe/DynDNS) by Golang and call it [GoDNS](https://github.com/TimothyYe/godns).
|
||||||
|
|
||||||
## Supported DNS Provider
|
## Supported DNS Provider
|
||||||
|
* Cloudflare ([https://cloudflare.com](https://cloudflare.com))
|
||||||
* DNSPod ([https://www.dnspod.cn/](https://www.dnspod.cn/))
|
* DNSPod ([https://www.dnspod.cn/](https://www.dnspod.cn/))
|
||||||
* HE.net (Hurricane Electric) ([https://dns.he.net/](https://dns.he.net/))
|
* HE.net (Hurricane Electric) ([https://dns.he.net/](https://dns.he.net/))
|
||||||
|
|
||||||
@ -95,6 +96,28 @@ Usage of ./godns:
|
|||||||
* Configure the SMTP options if you want, a mail notification will sent to your mailbox once the IP is changed.
|
* Configure the SMTP options if you want, a mail notification will sent to your mailbox once the IP is changed.
|
||||||
* Save it in the same directory of GoDNS, or use -c=your_conf_path command.
|
* Save it in the same directory of GoDNS, or use -c=your_conf_path command.
|
||||||
|
|
||||||
|
### Config example for Cloudflare
|
||||||
|
|
||||||
|
For Cloudflare, you need to provide email & Global API Key as password, and config all the domains & subdomains.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"provider": "Cloudflare",
|
||||||
|
"email": "you@example.com"
|
||||||
|
"password": "Global API Key",
|
||||||
|
"domains": [{
|
||||||
|
"domain_name": "example.com",
|
||||||
|
"sub_domains": ["www","test"]
|
||||||
|
},{
|
||||||
|
"domain_name": "example2.com",
|
||||||
|
"sub_domains": ["www","test"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ip_url": "https://ifconfig.co/ip",
|
||||||
|
"socks5_proxy": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Config example for DNSPod
|
### Config example for DNSPod
|
||||||
|
|
||||||
For DNSPod, you need to provide email & password, and config all the domains & subdomains.
|
For DNSPod, you need to provide email & password, and config all the domains & subdomains.
|
||||||
|
245
handler/cloudflare_handler.go
Normal file
245
handler/cloudflare_handler.go
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"runtime/debug"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/TimothyYe/godns"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CloudflareHandler struct definition
|
||||||
|
type CloudflareHandler struct {
|
||||||
|
Configuration *godns.Settings
|
||||||
|
API string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSRecordResponse struct
|
||||||
|
type DNSRecordResponse struct {
|
||||||
|
Records []DNSRecord `json:"result"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSRecordUpdateResponse struct
|
||||||
|
type DNSRecordUpdateResponse struct {
|
||||||
|
Record DNSRecord `json:"result"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSRecord for Cloudflare API
|
||||||
|
type DNSRecord struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
IP string `json:"content"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Proxied bool `json:"proxied"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
ZoneID string `json:"zone_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIP updates DNSRecord.IP
|
||||||
|
func (r *DNSRecord) SetIP(ip string) {
|
||||||
|
r.IP = ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneResponse is a wrapper for Zones
|
||||||
|
type ZoneResponse struct {
|
||||||
|
Zones []Zone `json:"result"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone object with id and name
|
||||||
|
type Zone struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetConfiguration pass dns settings and store it to handler instance
|
||||||
|
func (handler *CloudflareHandler) SetConfiguration(conf *godns.Settings) {
|
||||||
|
handler.Configuration = conf
|
||||||
|
handler.API = "https://api.cloudflare.com/client/v4"
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainLoop the main logic loop
|
||||||
|
func (handler *CloudflareHandler) DomainLoop(domain *godns.Domain, panicChan chan<- godns.Domain) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
log.Printf("Recovered in %v: %v\n", err, debug.Stack())
|
||||||
|
panicChan <- *domain
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
currentIP, err := godns.GetCurrentIP(handler.Configuration)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error in GetCurrentIP:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("Current IP is:", currentIP)
|
||||||
|
// TODO: check against locally cached IP, if no change, skip update
|
||||||
|
|
||||||
|
log.Println("Checking IP for domain", domain.DomainName)
|
||||||
|
zoneID := handler.getZone(domain.DomainName)
|
||||||
|
if zoneID != "" {
|
||||||
|
records := handler.getDNSRecords(zoneID)
|
||||||
|
|
||||||
|
// update records
|
||||||
|
for _, rec := range records {
|
||||||
|
if recordTracked(domain, &rec) != true {
|
||||||
|
log.Println("Skiping record:", rec.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rec.IP != currentIP {
|
||||||
|
log.Printf("IP mismatch: Current(%+v) vs Cloudflare(%+v)\r\n", currentIP, rec.IP)
|
||||||
|
handler.updateRecord(rec, currentIP)
|
||||||
|
} else {
|
||||||
|
log.Printf("Record OK: %+v - %+v\r\n", rec.Name, rec.IP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Println("Failed to find zone for domain:", domain.DomainName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interval is 5 minutes
|
||||||
|
log.Printf("Going to sleep, will start next checking in %d minutes...\r\n", godns.INTERVAL)
|
||||||
|
time.Sleep(time.Minute * godns.INTERVAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if record is present in domain conf
|
||||||
|
func recordTracked(domain *godns.Domain, record *DNSRecord) bool {
|
||||||
|
|
||||||
|
if record.Name == domain.DomainName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, subDomain := range domain.SubDomains {
|
||||||
|
sd := subDomain + "." + domain.DomainName
|
||||||
|
if record.Name == sd {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new request with auth in place and optional proxy
|
||||||
|
func (handler *CloudflareHandler) newRequest(method, url string, body io.Reader) (*http.Request, *http.Client) {
|
||||||
|
client := &http.Client{}
|
||||||
|
|
||||||
|
if handler.Configuration.Socks5Proxy != "" {
|
||||||
|
log.Println("use socks5 proxy:" + handler.Configuration.Socks5Proxy)
|
||||||
|
dialer, err := proxy.SOCKS5("tcp", handler.Configuration.Socks5Proxy, nil, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("can't connect to the proxy:", err)
|
||||||
|
} else {
|
||||||
|
httpTransport := &http.Transport{}
|
||||||
|
client.Transport = httpTransport
|
||||||
|
httpTransport.Dial = dialer.Dial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest(method, handler.API+url, body)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("X-Auth-Email", handler.Configuration.Email)
|
||||||
|
req.Header.Set("X-Auth-Key", handler.Configuration.Password)
|
||||||
|
return req, client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the correct zone via domain name
|
||||||
|
func (handler *CloudflareHandler) getZone(domain string) string {
|
||||||
|
|
||||||
|
var z ZoneResponse
|
||||||
|
|
||||||
|
req, client := handler.newRequest("GET", "/zones", nil)
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Request error:", err.Error())
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
err = json.Unmarshal(body, &z)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Decoder error: %+v\n", err)
|
||||||
|
log.Printf("Response body: %+v\n", string(body))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if z.Success != true {
|
||||||
|
log.Printf("Response failed: %+v\n", string(body))
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, zone := range z.Zones {
|
||||||
|
if zone.Name == domain {
|
||||||
|
return zone.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all DNS A records for a zone
|
||||||
|
func (handler *CloudflareHandler) getDNSRecords(zoneID string) []DNSRecord {
|
||||||
|
|
||||||
|
var empty []DNSRecord
|
||||||
|
var r DNSRecordResponse
|
||||||
|
|
||||||
|
req, client := handler.newRequest("GET", "/zones/"+zoneID+"/dns_records?type=A", nil)
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Request error:", err.Error())
|
||||||
|
return empty
|
||||||
|
}
|
||||||
|
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
err = json.Unmarshal(body, &r)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Decoder error: %+v\n", err)
|
||||||
|
log.Printf("Response body: %+v\n", string(body))
|
||||||
|
return empty
|
||||||
|
}
|
||||||
|
if r.Success != true {
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
log.Printf("Response failed: %+v\n", string(body))
|
||||||
|
return empty
|
||||||
|
|
||||||
|
}
|
||||||
|
return r.Records
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update DNS A Record with new IP
|
||||||
|
func (handler *CloudflareHandler) updateRecord(record DNSRecord, newIP string) {
|
||||||
|
|
||||||
|
var r DNSRecordUpdateResponse
|
||||||
|
record.SetIP(newIP)
|
||||||
|
|
||||||
|
j, _ := json.Marshal(record)
|
||||||
|
req, client := handler.newRequest("PUT",
|
||||||
|
"/zones/"+record.ZoneID+"/dns_records/"+record.ID,
|
||||||
|
bytes.NewBuffer(j),
|
||||||
|
)
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Request error:", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
err = json.Unmarshal(body, &r)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Decoder error: %+v\n", err)
|
||||||
|
log.Printf("Response body: %+v\n", string(body))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.Success != true {
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
log.Printf("Response failed: %+v\n", string(body))
|
||||||
|
} else {
|
||||||
|
log.Printf("Record updated: %+v - %+v", record.Name, record.IP)
|
||||||
|
}
|
||||||
|
}
|
167
handler/cloudflare_handler_test.go
Normal file
167
handler/cloudflare_handler_test.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/TimothyYe/godns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResponseToJSON(t *testing.T) {
|
||||||
|
s := strings.NewReader(`
|
||||||
|
{
|
||||||
|
"errors": [],
|
||||||
|
"messages": [],
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"id": "mk2b6fa491c12445a4376666a32429e1",
|
||||||
|
"name": "example.com",
|
||||||
|
"status": "active"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"result_info": {
|
||||||
|
"count": 1,
|
||||||
|
"page": 1,
|
||||||
|
"per_page": 20,
|
||||||
|
"total_count": 1,
|
||||||
|
"total_pages": 1
|
||||||
|
},
|
||||||
|
"success": true
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var resp ZoneResponse
|
||||||
|
err := json.NewDecoder(s).Decode(&resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
if resp.Success != true {
|
||||||
|
t.Errorf("Success Error: %#v != true ", resp.Success)
|
||||||
|
}
|
||||||
|
if resp.Zones[0].ID != "mk2b6fa491c12445a4376666a32429e1" {
|
||||||
|
t.Errorf("ID Error: %#v != mk2b6fa491c12445a4376666a32429e1 ", resp.Zones[0].ID)
|
||||||
|
}
|
||||||
|
if resp.Zones[0].Name != "example.com" {
|
||||||
|
t.Errorf("Name Error: %#v != example.com", resp.Zones[0].Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDNSResponseToJSON(t *testing.T) {
|
||||||
|
s := strings.NewReader(`
|
||||||
|
{
|
||||||
|
"errors": [],
|
||||||
|
"messages": [],
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"content": "127.0.0.1",
|
||||||
|
"id": "F11cc63e02a42d38174b8e7c548a7b6f",
|
||||||
|
"name": "example.com",
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "mk2b6fa491c12445a4376666a32429e1",
|
||||||
|
"zone_name": "example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"success": true
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var resp DNSRecordResponse
|
||||||
|
err := json.NewDecoder(s).Decode(&resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
if resp.Success != true {
|
||||||
|
t.Errorf("Success Error: %#v != true ", resp.Success)
|
||||||
|
}
|
||||||
|
if resp.Records[0].ID != "F11cc63e02a42d38174b8e7c548a7b6f" {
|
||||||
|
t.Errorf("ID Error: %#v != F11cc63e02a42d38174b8e7c548a7b6f ", resp.Records[0].ID)
|
||||||
|
}
|
||||||
|
if resp.Records[0].Name != "example.com" {
|
||||||
|
t.Errorf("Name Error: %#v != example.com", resp.Records[0].Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestDNSUpdateResponseToJSON(t *testing.T) {
|
||||||
|
s := strings.NewReader(`
|
||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"id": "F11cc63e02a42d38174b8e7c548a7b6f",
|
||||||
|
"type": "A",
|
||||||
|
"name": "example.com",
|
||||||
|
"content": "127.0.0.1",
|
||||||
|
"proxiable": true,
|
||||||
|
"proxied": true,
|
||||||
|
"ttl": 1,
|
||||||
|
"locked": false,
|
||||||
|
"zone_id": "mk2b6fa491c12445a4376666a32429e1",
|
||||||
|
"zone_name": "example.com",
|
||||||
|
"modified_on": "2018-10-12T14:29:53.205191Z",
|
||||||
|
"created_on": "2018-10-12T14:29:53.205191Z",
|
||||||
|
"meta": {
|
||||||
|
"auto_added": false,
|
||||||
|
"managed_by_apps": false,
|
||||||
|
"managed_by_argo_tunnel": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"success": true,
|
||||||
|
"errors": [],
|
||||||
|
"messages": []
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var resp DNSRecordUpdateResponse
|
||||||
|
err := json.NewDecoder(s).Decode(&resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
if resp.Success != true {
|
||||||
|
t.Errorf("Success Error: %#v != true ", resp.Success)
|
||||||
|
}
|
||||||
|
if resp.Record.ID != "F11cc63e02a42d38174b8e7c548a7b6f" {
|
||||||
|
t.Errorf("ID Error: %#v != F11cc63e02a42d38174b8e7c548a7b6f ", resp.Record.ID)
|
||||||
|
}
|
||||||
|
if resp.Record.Name != "example.com" {
|
||||||
|
t.Errorf("Name Error: %#v != example.com", resp.Record.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRecordTracked(t *testing.T) {
|
||||||
|
s := strings.NewReader(`
|
||||||
|
{
|
||||||
|
"errors": [],
|
||||||
|
"messages": [],
|
||||||
|
"result": [
|
||||||
|
{
|
||||||
|
"content": "127.0.0.1",
|
||||||
|
"id": "F11cc63e02a42d38174b8e7c548a7b6f",
|
||||||
|
"name": "example.com",
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "mk2b6fa491c12445a4376666a32429e1",
|
||||||
|
"zone_name": "example.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"content": "127.0.0.1",
|
||||||
|
"id": "G00cc63e02a42d38174b8e7c548a7b6f",
|
||||||
|
"name": "www.example.com",
|
||||||
|
"type": "A",
|
||||||
|
"zone_id": "mk2b6fa491c12445a4376666a32429e1",
|
||||||
|
"zone_name": "www.example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"success": true
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var resp DNSRecordResponse
|
||||||
|
err := json.NewDecoder(s).Decode(&resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := &godns.Domain{
|
||||||
|
DomainName: "example.com",
|
||||||
|
SubDomains: []string{"www"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rec := range resp.Records {
|
||||||
|
if recordTracked(domain, &rec) != true {
|
||||||
|
t.Errorf("invalid record skip: %+v\r\n", rec.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,8 @@ func CreateHandler(provider string) IHandler {
|
|||||||
var handler IHandler
|
var handler IHandler
|
||||||
|
|
||||||
switch provider {
|
switch provider {
|
||||||
|
case godns.CLOUDFLARE:
|
||||||
|
handler = IHandler(&CloudflareHandler{})
|
||||||
case godns.DNSPOD:
|
case godns.DNSPOD:
|
||||||
handler = IHandler(&DNSPodHandler{})
|
handler = IHandler(&DNSPodHandler{})
|
||||||
case godns.HE:
|
case godns.HE:
|
||||||
|
@ -25,6 +25,7 @@ type Notify struct {
|
|||||||
// Settings struct
|
// Settings struct
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
LoginToken string `json:"login_token"`
|
LoginToken string `json:"login_token"`
|
||||||
Domains []Domain `json:"domains"`
|
Domains []Domain `json:"domains"`
|
||||||
|
12
utils.go
12
utils.go
@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
"gopkg.in/gomail.v2"
|
"gopkg.in/gomail.v2"
|
||||||
@ -38,6 +39,8 @@ const (
|
|||||||
DNSPOD = "DNSPod"
|
DNSPOD = "DNSPod"
|
||||||
// HE for he.net
|
// HE for he.net
|
||||||
HE = "HE"
|
HE = "HE"
|
||||||
|
// CLOUDFLARE for cloudflare.com
|
||||||
|
CLOUDFLARE = "Cloudflare"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCurrentIP gets public IP from internet
|
// GetCurrentIP gets public IP from internet
|
||||||
@ -68,7 +71,7 @@ func GetCurrentIP(configuration *Settings) (string, error) {
|
|||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
|
|
||||||
body, _ := ioutil.ReadAll(response.Body)
|
body, _ := ioutil.ReadAll(response.Body)
|
||||||
return string(body), nil
|
return strings.Trim(string(body), "\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckSettings check the format of settings
|
// CheckSettings check the format of settings
|
||||||
@ -81,6 +84,13 @@ func CheckSettings(config *Settings) error {
|
|||||||
if config.Password == "" {
|
if config.Password == "" {
|
||||||
return errors.New("password cannot be empty")
|
return errors.New("password cannot be empty")
|
||||||
}
|
}
|
||||||
|
} else if config.Provider == CLOUDFLARE {
|
||||||
|
if config.Email == "" {
|
||||||
|
return errors.New("email cannot be empty")
|
||||||
|
}
|
||||||
|
if config.Password == "" {
|
||||||
|
return errors.New("password cannot be empty")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("please provide supported DNS provider: DNSPod/HE")
|
return errors.New("please provide supported DNS provider: DNSPod/HE")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user