diff --git a/config_sample.json b/config_sample.json index 7eb6785..4288d06 100644 --- a/config_sample.json +++ b/config_sample.json @@ -19,6 +19,7 @@ } ], "ip_url": "http://members.3322.org/dyndns/getip", + "ip_interface": "eth0", "socks5_proxy": "", "notify": { "enabled": false, diff --git a/handler/cloudflare_handler.go b/handler/cloudflare_handler.go index ba0346f..1ee4da3 100644 --- a/handler/cloudflare_handler.go +++ b/handler/cloudflare_handler.go @@ -74,6 +74,7 @@ func (handler *CloudflareHandler) DomainLoop(domain *godns.Domain, panicChan cha } }() + var lastIP string for { currentIP, err := godns.GetCurrentIP(handler.Configuration) if err != nil { @@ -81,30 +82,34 @@ func (handler *CloudflareHandler) DomainLoop(domain *godns.Domain, panicChan cha 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) - } - } + //check against locally cached IP, if no change, skip update + if (currentIP == lastIP){ + log.Printf("IP is the same as cached one. Skip update.\n") } else { - log.Println("Failed to find zone for domain:", domain.DomainName) - } + lastIP = currentIP + 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) diff --git a/handler/dnspod_handler.go b/handler/dnspod_handler.go index 590a277..ea116da 100644 --- a/handler/dnspod_handler.go +++ b/handler/dnspod_handler.go @@ -36,6 +36,7 @@ func (handler *DNSPodHandler) DomainLoop(domain *godns.Domain, panicChan chan<- } }() + var lastIP string for { log.Printf("Checking IP for domain %s \r\n", domain.DomainName) domainID := handler.GetDomain(domain.DomainName) @@ -52,31 +53,37 @@ func (handler *DNSPodHandler) DomainLoop(domain *godns.Domain, panicChan chan<- } log.Println("currentIP is:", currentIP) - for _, subDomain := range domain.SubDomains { + //check against locally cached IP, if no change, skip update + if (currentIP == lastIP){ + log.Printf("IP is the same as cached one. Skip update.\n") + }else{ + lastIP = currentIP + + for _, subDomain := range domain.SubDomains { - subDomainID, ip := handler.GetSubDomain(domainID, subDomain) + subDomainID, ip := handler.GetSubDomain(domainID, subDomain) - if subDomainID == "" || ip == "" { - log.Printf("domain: %s.%s subDomainID: %s ip: %s\n", subDomain, domain.DomainName, subDomainID, ip) - continue - } - - // Continue to check the IP of sub-domain - if len(ip) > 0 && strings.TrimRight(currentIP, "\n") != strings.TrimRight(ip, "\n") { - log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName) - handler.UpdateIP(domainID, subDomainID, subDomain, currentIP) - - // Send mail notification if notify is enabled - if handler.Configuration.Notify.Enabled { - log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) - godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP) + if subDomainID == "" || ip == "" { + log.Printf("domain: %s.%s subDomainID: %s ip: %s\n", subDomain, domain.DomainName, subDomainID, ip) + continue } - } else { - log.Printf("%s.%s Current IP is same as domain IP, no need to update...\n", subDomain, domain.DomainName) - } - } + // Continue to check the IP of sub-domain + if len(ip) > 0 && strings.TrimRight(currentIP, "\n") != strings.TrimRight(ip, "\n") { + log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName) + handler.UpdateIP(domainID, subDomainID, subDomain, currentIP) + // Send mail notification if notify is enabled + if handler.Configuration.Notify.Enabled { + log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) + godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP) + } + + } else { + log.Printf("%s.%s Current IP is same as domain IP, no need to update...\n", subDomain, 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) diff --git a/handler/he_handler.go b/handler/he_handler.go index c34e02c..56dc92a 100644 --- a/handler/he_handler.go +++ b/handler/he_handler.go @@ -39,6 +39,7 @@ func (handler *HEHandler) DomainLoop(domain *godns.Domain, panicChan chan<- godn } }() + var lastIP string for { currentIP, err := godns.GetCurrentIP(handler.Configuration) @@ -48,21 +49,28 @@ func (handler *HEHandler) DomainLoop(domain *godns.Domain, panicChan chan<- godn } log.Println("currentIP is:", currentIP) - for _, subDomain := range domain.SubDomains { - log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName) - handler.UpdateIP(domain.DomainName, subDomain, currentIP) + //check against locally cached IP, if no change, skip update + if (currentIP == lastIP){ + log.Printf("IP is the same as cached one. Skip update.\n") + } else { + lastIP = currentIP + + for _, subDomain := range domain.SubDomains { + log.Printf("%s.%s Start to update record IP...\n", subDomain, domain.DomainName) + handler.UpdateIP(domain.DomainName, subDomain, currentIP) - // Send mail notification if notify is enabled - if handler.Configuration.Notify.Enabled { - log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) - godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP) + // Send mail notification if notify is enabled + if handler.Configuration.Notify.Enabled { + log.Print("Sending notification to:", handler.Configuration.Notify.SendTo) + godns.SendNotify(handler.Configuration, fmt.Sprintf("%s.%s", subDomain, domain.DomainName), currentIP) + } } } - // 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) } + } // UpdateIP update subdomain with current IP diff --git a/settings.go b/settings.go index c287628..897e91b 100644 --- a/settings.go +++ b/settings.go @@ -33,6 +33,9 @@ type Settings struct { LogPath string `json:"log_path"` Socks5Proxy string `json:"socks5_proxy"` Notify Notify `json:"notify"` + IPInterface string `json:"ip_interface"` + //the code is not ready to update AAAA record + //IPType string `json:"ip_type"` } // LoadSettings -- Load settings from config file diff --git a/utils.go b/utils.go index af69132..f2c1808 100644 --- a/utils.go +++ b/utils.go @@ -8,6 +8,7 @@ import ( "log" "net/http" "strings" + "net" "golang.org/x/net/proxy" "gopkg.in/gomail.v2" @@ -43,8 +44,87 @@ const ( CLOUDFLARE = "Cloudflare" ) -// GetCurrentIP gets public IP from internet +//GetIPFromInterface gets IP address from the specific interface +func GetIPFromInterface(configuration *Settings) (string,error) { + ifaces, err := net.InterfaceByName(configuration.IPInterface) + if err != nil { + log.Println("can't get network device "+configuration.IPInterface+":", err) + return "", err + } + + addrs, err :=ifaces.Addrs(); + if err != nil { + log.Println("can't get address from "+configuration.IPInterface+":", err) + return "", err + } + + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if (ip == nil){ + continue; + } + + if (!(ip.IsGlobalUnicast() &&!(ip.IsUnspecified()||ip.IsMulticast()||ip.IsLoopback()||ip.IsLinkLocalUnicast()||ip.IsLinkLocalMulticast()||ip.IsInterfaceLocalMulticast()))){ + continue; + } + + //the code is not ready for updating an AAAA record + /* + if (isIPv4(ip.String())){ + if (configuration.IPType=="IPv6"){ + continue; + } + }else{ + if (configuration.IPType!="IPv6"){ + continue; + } + } */ + if (!isIPv4(ip.String())){ + continue; + } + + return ip.String(),nil + + } + return "", errors.New("can't get a vaild address from "+configuration.IPInterface) +} + +func isIPv4(ip string) bool{ + return strings.Count(ip, ":") < 2 +} + +//GetCurrentIP gets an IP from either internet or specific interface, depending on configuration func GetCurrentIP(configuration *Settings) (string, error) { + var err error + + if (configuration.IPUrl != ""){ + ip, err := GetIPOnline(configuration) + if (err != nil){ + log.Println("get ip online failed. Fallback to get ip from interface if possible.") + }else{ + return ip,nil + } + } + + if (configuration.IPInterface != ""){ + ip, err := GetIPFromInterface(configuration) + if (err != nil){ + log.Println("get ip from interface failed. There is no more ways to try.") + }else{ + return ip,nil + } + } + + return "", err +} +// GetIPOnline gets public IP from internet +func GetIPOnline(configuration *Settings) (string, error) { client := &http.Client{} if configuration.Socks5Proxy != "" {