Skip to content

Commit

Permalink
Add ipv6 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Hentra committed May 17, 2020
1 parent 78c7dff commit ef574a7
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 48 deletions.
33 changes: 20 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@ related to netcup GmbH. It is **heavily** inspired by
project which might be also a good solution for your
dynamic dns needs.


## Table of Contents
<!-- vim-markdown-toc GFM -->

* [Features](#features)
* [Implemented](#implemented)
* [Missing](#missing)
* [Implemented](#implemented)
* [Missing](#missing)
* [Installation](#installation)
* [Manual](#manual)
* [From source](#from-source)
* [Manual](#manual)
* [From source](#from-source)
* [Usage](#usage)
* [Prequisites](#prequisites)
* [Run dyndns-netcup-go](#run-dyndns-netcup-go)
* [Commandline flags](#commandline-flags)
* [Prequisites](#prequisites)
* [Run dyndns-netcup-go](#run-dyndns-netcup-go)
* [Commandline flags](#commandline-flags)
* [Contributing](#contributing)

<!-- vim-markdown-toc -->
Expand All @@ -35,10 +34,15 @@ dynamic dns needs.
* TTL update support
* Creation of a DNS record if it doesn't already exists.
* Multi host support (nice when you need to update both `@` and `*`)
* IPv6 support
* Verbose option (when you specify `-v` you get alot of information)

### Missing
* IPv6 support
* Quiet option (output is always really verbose)

* MX entry support

There are currently no plans to implement this features. If you need those (or additional features) please
open up an [Issue](https://github.com/Hentra/dyndns-netcup-go/issues).

## Installation

Expand All @@ -51,16 +55,19 @@ dynamic dns needs.
First, install [Go](https://golang.org/doc/install) as
recommended. After that run following commands:

git clone https://github.com/Hentra/dyndns-netcup-go.git cd
dyndns-netcup-go
git clone https://github.com/Hentra/dyndns-netcup-go.git
cd dyndns-netcup-go
go install

This will create a binary named `dyndns-netcup-go` and install it to your go binary home.
This will create a binary named `dyndns-netcup-go` and install it to your go
binary home. Make sure your `GOPATH` environment variable is set.

Refer to [Usage](#usage) for further information.

## Usage

### Prequisites

* You need to have a netcup account and a domain, obviously.
* Then you need an apikey and apipassword.
[Here](https://www.netcup-wiki.de/wiki/CCP_API#Authentifizierung) is a
Expand Down
12 changes: 8 additions & 4 deletions example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ CUSTOMERNR: 12345
APIKEY: 'yourapikey'
APIPASSWORD: 'yourapipassword'

DOMAINS:
- NAME: 'example.de' # Your domain name without any subdomains
IPV6: false # Not supported yet
DOMAINS:
- NAME: 'example.de' # Your domain name without any subdomains.
IPV6: true # Whether the 'AAAA' entries of this host should be
# updated with the IPv6 address or not.
TTL: 300 # Time to live for this zone. Around 300 is good for dyndns.
HOSTS: # Every host that should get your public ip
- '@'
- '*'
- 'cool.subdomain'
- 'cool.subdomain' # You could also specify subdomains longer than this.

- NAME: 'example.com'
IPV6: false
TTL: 350
HOSTS:
- '@'

# You can add how many domains as you like. Keep in mind that more domains also
# means more request -> more time.
10 changes: 8 additions & 2 deletions ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ import (
"net/http"
)

func getIP() (string, error) {
url := "https://api.ipify.org?format=text"
func getIPv4() (string, error) {
return do("https://api.ipify.org?format=text")
}

func getIPv6() (string, error) {
return do("https://api6.ipify.org?format=text")
}

func do(url string) (string, error) {
resp, err := http.Get(url)
if err != nil {
return "", err
Expand Down
123 changes: 98 additions & 25 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import(

var (
configFile string
config *Config
ipv4 string
ipv6 string
verbose bool
client *netcup.Client
)

const (
Expand All @@ -18,8 +22,16 @@ const (
verboseUsage = "Use verbose output"
)


func main() {
login()

loadIPv4()
loadIPv6()

configureDomains()
}

func init() {
flag.StringVar(&configFile, "config", defaultConfigFile, configUsage)
flag.StringVar(&configFile, "c", defaultConfigFile, configUsage + " (shorthand)")

Expand All @@ -30,26 +42,50 @@ func main() {

netcup.SetVerbose(verbose)

config, err := LoadConfig(configFile)
var err error
config, err = LoadConfig(configFile)
if err != nil {
log.Fatal(err)
}
}

client := netcup.NewClient(config.CustomerNumber, config.ApiKey, config.ApiPassword)
func login() {
client = netcup.NewClient(config.CustomerNumber, config.ApiKey, config.ApiPassword)
err := client.Login()
if err != nil {
log.Fatal(err)
}
}

err = client.Login()
func loadIPv4() {
logInfo("Loading public IPv4 address")
var err error
ipv4, err = getIPv4()
if err != nil {
log.Fatal(err)
}
logInfo("Public IPv4 address is %s", ipv4)
}

logInfo("Loading public IP address")
ip, err := getIP()
func loadIPv6() {
logInfo("Loading public IPv6 address")
var err error
ipv6, err = getIPv6()
if err != nil {
log.Fatal(err)
}
logInfo("Public IP address is %s", ip)
logInfo("Public IPv6 address is %s", ipv6)

}

func configureDomains() {
for _, domain := range config.Domains {
configureZone(domain)
configureRecords(domain)
}
}

func configureZone(domain Domain) {
logInfo("Loading DNS Zone info for domain %s", domain.Name)
err, zone := client.InfoDnsZone(domain.Name)
if err != nil {
Expand All @@ -70,7 +106,9 @@ func main() {
log.Fatal(err)
}
}
}

func configureRecords(domain Domain) {
logInfo("Loading DNS Records for domain %s", domain.Name)
err, records := client.InfoDnsRecords(domain.Name)
if err != nil {
Expand All @@ -79,27 +117,23 @@ func main() {

var updateRecords []netcup.DNSRecord
for _, host := range domain.Hosts {
if records.GetARecordOccurences(host) > 1 {
if records.GetRecordOccurences(host, "A") > 1 {
logInfo("Too many A records for host '%s'. Please specify only Hosts with one corresponding A record", host)
continue
} else {
newRecord, needsUpdate := configureARecord(host, records)
if needsUpdate {
updateRecords = append(updateRecords, *newRecord)
}
}
if record, exists := records.GetARecord(host); exists {
logInfo("Found one A record for host '%s'.", host)
if record.Destination != ip {
logInfo("IP address of host '%s' is %s but should be %s. Queue for update...", host, record.Destination, ip)
record.Destination = ip
updateRecords = append(updateRecords, *record)
if domain.IPv6 {
if records.GetRecordOccurences(host, "AAAA") > 1 {
logInfo("Too many AAAA records for host '%s'. Please specify only Hosts with one corresponding AAAA record", host)
} else {
logInfo("Destination of host '%s' is already public ip %s", host, ip)
newRecord, needsUpdate := configureAAAARecord(host, records)
if needsUpdate {
updateRecords = append(updateRecords, *newRecord)
}
}
} else {
logInfo("There is no A record for '%s'. Creating and queueing for update", host)
record := netcup.DNSRecord{
Hostname: host,
Type: "A",
Destination: ip,
}
updateRecords = append(updateRecords, record)
}
}

Expand All @@ -113,7 +147,46 @@ func main() {
} else {
logInfo("No updates queued.")
}
}
}

func configureARecord(host string, records *netcup.DNSRecordSet) (*netcup.DNSRecord, bool) {
var result *netcup.DNSRecord
if record, exists := records.GetRecord(host, "A"); exists {
logInfo("Found one A record for host '%s'.", host)
if record.Destination != ipv4 {
logInfo("IP address of host '%s' is %s but should be %s. Queue for update...", host, record.Destination, ipv4)
record.Destination = ipv4
result = record
} else {
logInfo("Destination of host '%s' is already public IPv4 %s", host, ipv4)
return nil, false
}
} else {
logInfo("There is no A record for '%s'. Creating and queueing for update", host)
result = netcup.NewDNSRecord(host, "A", ipv4)
}

return result, true
}

func configureAAAARecord(host string, records *netcup.DNSRecordSet) (*netcup.DNSRecord, bool) {
var result *netcup.DNSRecord
if record, exists := records.GetRecord(host, "AAAA"); exists {
logInfo("Found one AAAA record for host '%s'.", host)
if record.Destination != ipv6 {
logInfo("IP address of host '%s' is %s but should be %s. Queue for update...", host, record.Destination, ipv6)
record.Destination = ipv6
result = record
} else {
logInfo("Destination of host '%s' is already public IPv6 %s", host, ipv6)
return nil, false
}
} else {
logInfo("There is no AAAA record for '%s'. Creating and queueing for update", host)
result = netcup.NewDNSRecord(host, "AAAA", ipv6)
}

return result, true
}

func logInfo(msg string, v ...interface{}) {
Expand Down
16 changes: 12 additions & 4 deletions netcup/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,33 @@ func NewDNSRecordSet(records []DNSRecord) *DNSRecordSet {
}
}

func (r *DNSRecordSet) GetARecordOccurences(hostname string) int {
func (r *DNSRecordSet) GetRecordOccurences(hostname, dnstype string) int {
result := 0
for _, record := range r.DNSRecords {
if record.Hostname == hostname && record.Type == "A" {
if record.Hostname == hostname && record.Type == dnstype {
result++
}
}
return result
}

func (r *DNSRecordSet) GetARecord(name string) (*DNSRecord, bool) {
func (r *DNSRecordSet) GetRecord(name, dnstype string) (*DNSRecord, bool) {
for _, record := range r.DNSRecords {
if record.Hostname == name && record.Type == "A" {
if record.Hostname == name && record.Type == dnstype {
return &record, true
}
}
return nil, false
}

func NewDNSRecord(hostname, dnstype, destination string) *DNSRecord {
return &DNSRecord{
Hostname: hostname,
Type: dnstype,
Destination: destination,
}
}

func (r *Response) isError() bool {
return r.Status == "error"
}
Expand Down

0 comments on commit ef574a7

Please sign in to comment.