Get external IP and update ddns service I've created a cli application that consists of a package with 2 functions. One to get the external ip address, and another function to update a dynamic dns record at dns.he.net. Both are then used in main based on the provided parameters.

Since this is my first "real" go application i would like to know if I'm using structs, interfaces and functions correctly, as well as the overal structure of the application and if stuff should move into the functions or should go into main.

(Since it is tailor-made for my specific case I've hardcoded the remote url's.)

All feedback is welcome.

usage:

dyn-he-updater get --ipv4

dyn-he-updater update --domain=example.com --password=skdffngkele --ipv4

dyn.go

package dyn

import (
    "crypto/tls"
    "github.com/go-resty/resty"
    "log"
    "net"
    "strings"
)

type Dyn interface {
    GetIp() string
    UpdateIp()
}

type He struct {
    Hostname string
    Password string
    Ip       net.IP
}

func GetIp(uri string) net.IP {
    resp, err := resty.R().Get(uri)
    if err != nil {
        log.Fatal("ERROR: %s", err)
    }

    ip := net.ParseIP(strings.TrimSpace(string(resp.Body())))

    if ip == nil {
        log.Fatal("ERROR: Failed to parse the IP.")
    }
    return ip
}

func (h *He) UpdateIp(uri string) {
    log.Println("INFO: updating the ip")
    log.Printf("INFO: URI: %s \nDomain: %s \nPassword: %s \nIP: %v\n", uri, h.Hostname, h.Password, h.Ip)

    resty.SetTLSClientConfig(&amp;tls.Config{InsecureSkipVerify: true})

    resp, err := resty.R().
        SetFormData(map[string]string{
            "hostname": h.Hostname,
            "password": h.Password,
            "myip":     h.Ip.String(),
        }).
        Post(uri)
    if err != nil {
        log.Fatal("ERROR: ", err)
    }

    log.Printf("INFO: resp: %v", string(resp.Body()))
}


main.go

package main

import (
    "fmt"
    "github.com/Adamar/dyn-he-updater/dyn"
    "gopkg.in/alecthomas/kingpin.v2"
    "net"
    "os"
)

var (
    app   = kingpin.New("dyn-he-updater", "A commandline updater for dynamic ip's from dyn.dns.he.net")
    debug = app.Flag("debug", "Enable debug mode.").Bool()

    get   = app.Command("get", "Get the raw ip from an external service.")
    getV4 = get.Flag("ipv4", "Get only the IPv4 ip address.").Bool()
    getV6 = get.Flag("ipv6", "Get only the IPv6 ip address.").Bool()

    update = app.Command("update", "Update the dynamic ip.")
    domain = update.Flag("domain", "Domain you want to update.").Required().String()
    pass   = update.Flag("password", "Password to update the domain.").Required().String()
    v4     = update.Flag("ipv4", "Update only the IPv4 ip address. (default)").Bool()
    v6     = update.Flag("ipv6", "Update only the IPv6 ip address.").Bool()

    CheckIPv4Uri = "http://ipv4.myexternalip.com/raw"
    CheckIPv6Uri = "http://ipv6.myexternalip.com/raw"
    UpdateIpUri  = "https://dyn.dns.he.net/nic/update"
)

func main() {
    kingpin.Version("0.0.1")

    switch kingpin.MustParse(app.Parse(os.Args[1:])) {

    case get.FullCommand():

        if *getV4 == true {
            r := dyn.GetIp(CheckIPv4Uri)
            fmt.Printf("IPv4: %s\n", r)
        } else if *getV6 {
            r := dyn.GetIp(CheckIPv6Uri)
            fmt.Printf("IPv6: %s\n", r)
        } else {
            r := dyn.GetIp(CheckIPv4Uri)
            fmt.Printf("IPv4: %s (default)\n", r)
        }

    case update.FullCommand():
        if *domain != "" &amp;&amp; *pass != "" {

            var r net.IP
            if *v4 == true || *v6 == false {
                r = dyn.GetIp(CheckIPv4Uri)
            } else if *v6 == true {
                r = dyn.GetIp(CheckIPv6Uri)
            }

            d := dyn.He{Hostname: *domain, Password: *pass, Ip: r}
            d.UpdateIp(UpdateIpUri)
        }
    }
}
I find it annoying that you can't update both the IPv4 and IPv6 records in just one invocation of your program.

According to the usage guide at https://dns.he.net,


Hurricane Electric also provides a "what is my IP address?" service, at http://checkip.dns.he.net/ .  That way, your program wouldn't be relying on a second service provider.
Better yet, this dynamic DNS service automatically infers that you want to add a DNS record that refers to your current IP address, if you omit the myip parameter.


Autodetect my IPv4/IPv6 address:
% curl -4 "http://dyn.example.com:password@dyn.dns.he.net/nic/update?hostname=dyn.example.com"
% curl -6 "http://dyn.example.com:password@dyn.dns.he.net/nic/update?hostname=dyn.example.com"