dyndnsd-client.go 3.57 KB
Newer Older
Erick Hitter's avatar
Erick Hitter committed
1 2
package main

Erick Hitter's avatar
Erick Hitter committed
3
import (
Erick Hitter's avatar
Erick Hitter committed
4
	"flag"
Erick Hitter's avatar
Erick Hitter committed
5 6 7
	"fmt"
	"github.com/joshbetz/config"
	"io/ioutil"
Erick Hitter's avatar
Erick Hitter committed
8
	"log"
Erick Hitter's avatar
Erick Hitter committed
9
	"net"
Erick Hitter's avatar
Erick Hitter committed
10 11
	"net/http"
	"net/url"
Erick Hitter's avatar
Erick Hitter committed
12
	"os"
13
	"strings"
Erick Hitter's avatar
Erick Hitter committed
14 15 16 17 18 19
)

var (
	cfg          *config.Config
	ipv4Endpoint string
	ipv6Endpoint string
Erick Hitter's avatar
Erick Hitter committed
20 21

	logger *log.Logger
Erick Hitter's avatar
Erick Hitter committed
22 23
)

Erick Hitter's avatar
Erick Hitter committed
24
// Preparations
Erick Hitter's avatar
Erick Hitter committed
25
func init() {
Erick Hitter's avatar
Erick Hitter committed
26 27 28 29 30
	// Logging
	logOpts := log.Ldate | log.Ltime | log.LUTC | log.Lshortfile
	logger = log.New(os.Stdout, "", logOpts)

	logger.Println("PINGING dyndnsd ENDPOINT")
Erick Hitter's avatar
Erick Hitter committed
31

Erick Hitter's avatar
Erick Hitter committed
32
	// Configuration
Erick Hitter's avatar
Erick Hitter committed
33 34 35 36 37
	var configPath string
	flag.StringVar(&configPath, "config", "", "Config file path")
	flag.Parse()

	if _, err := os.Stat(configPath); os.IsNotExist(err) {
Erick Hitter's avatar
Erick Hitter committed
38
		fmt.Println("Config path does not exist. Aborting!")
Erick Hitter's avatar
Erick Hitter committed
39 40 41 42 43
		flag.Usage()
		os.Exit(3)
	}

	cfg = config.New(configPath)
Erick Hitter's avatar
Erick Hitter committed
44 45 46 47 48
	cfg.Get("ipv4_endpoint", &ipv4Endpoint)
	cfg.Get("ipv6_endpoint", &ipv6Endpoint)
}

// Do the update!
Erick Hitter's avatar
Erick Hitter committed
49
func main() {
Erick Hitter's avatar
Erick Hitter committed
50
	// Base URL
Erick Hitter's avatar
Erick Hitter committed
51
	endpoint, err := buildEndpointURL()
Erick Hitter's avatar
Erick Hitter committed
52
	if err != nil {
Erick Hitter's avatar
Erick Hitter committed
53 54
		logger.Println("Couldn't build endpoint URL")
		logger.Printf("%s", err)
Erick Hitter's avatar
Erick Hitter committed
55 56 57 58
		return
	}

	// IPv4 is required
Erick Hitter's avatar
Erick Hitter committed
59
	if ipv4, err := getURL(ipv4Endpoint); err == nil {
Erick Hitter's avatar
Erick Hitter committed
60 61
		ipv4Valid := net.ParseIP(ipv4)
		if ipv4Valid == nil {
Erick Hitter's avatar
Erick Hitter committed
62
			logger.Println("Invalid IPv4 address returned by endpoint")
Erick Hitter's avatar
Erick Hitter committed
63 64 65
			logger.Printf("%s", err)
			return
		}
Erick Hitter's avatar
Erick Hitter committed
66 67 68 69

		query := endpoint.Query()
		query.Set("myip", ipv4Valid.String())
		endpoint.RawQuery = query.Encode()
Erick Hitter's avatar
Erick Hitter committed
70
	} else {
Erick Hitter's avatar
Erick Hitter committed
71 72
		logger.Println("Couldn't retrieve IPv4 address")
		logger.Printf("%s", err)
Erick Hitter's avatar
Erick Hitter committed
73 74 75 76
		return
	}

	// IPv6 is optional
Erick Hitter's avatar
Erick Hitter committed
77 78
	// Leave empty to skip
	if len(ipv6Endpoint) > 0 {
Erick Hitter's avatar
Erick Hitter committed
79
		if ipv6, err := getURL(ipv6Endpoint); err == nil {
Erick Hitter's avatar
Erick Hitter committed
80 81 82 83 84 85 86 87
			if ipv6Valid := net.ParseIP(ipv6); ipv6Valid == nil {
				logger.Println("Invalid IPv6 address returned by endpoint")
				logger.Printf("%s", err)
			} else {
				var ipv6String string
				var usePrefix bool
				cfg.Get("ipv6_use_prefix", &usePrefix)
				ipMask := fmt.Sprintf("%s/%d", ipv6Valid.String(), 64)
Erick Hitter's avatar
Erick Hitter committed
88

Erick Hitter's avatar
Erick Hitter committed
89 90
				if ipv6, network, err := net.ParseCIDR(ipMask); usePrefix && err == nil {
					ipv6String = network.String()
91
					ipv6String = strings.Replace(ipv6String, "/64", "1", 1)
Erick Hitter's avatar
Erick Hitter committed
92 93 94 95 96 97 98 99 100 101 102
				} else {
					ipv6String = ipv6.String()
				}

				query := endpoint.Query()
				query.Set("myip6", ipv6String)
				endpoint.RawQuery = query.Encode()
			}
		} else {
			logger.Println("Couldn't retrieve IPv6 address")
			logger.Printf("%s", err)
Erick Hitter's avatar
Erick Hitter committed
103
		}
Erick Hitter's avatar
Erick Hitter committed
104 105 106
	}

	// Send the update
Erick Hitter's avatar
Erick Hitter committed
107
	dyndns, err := getURL(endpoint.String())
Erick Hitter's avatar
Erick Hitter committed
108
	if err != nil {
Erick Hitter's avatar
Erick Hitter committed
109 110
		logger.Println("Couldn't update dyndnsd endpoint")
		logger.Printf("%s", err)
Erick Hitter's avatar
Erick Hitter committed
111 112 113
		return
	}

Erick Hitter's avatar
Erick Hitter committed
114 115
	logger.Println("SUCCESS! Ping sent.")
	logger.Printf("%s", dyndns)
Erick Hitter's avatar
Erick Hitter committed
116 117 118
}

// Build endpoint URL from configuration
Erick Hitter's avatar
Erick Hitter committed
119
func buildEndpointURL() (*url.URL, error) {
Erick Hitter's avatar
Erick Hitter committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
	var username string
	var password string
	var protocol string
	var host string
	var port int
	var path string
	var hostname string

	cfg.Get("username", &username)
	cfg.Get("password", &password)
	cfg.Get("protocol", &protocol)
	cfg.Get("host", &host)
	cfg.Get("port", &port)
	cfg.Get("path", &path)
	cfg.Get("dns_hostname", &hostname)

Erick Hitter's avatar
Erick Hitter committed
136
	daemonURL, err := url.Parse("")
Erick Hitter's avatar
Erick Hitter committed
137 138 139 140
	if err != nil {
		return nil, err
	}

Erick Hitter's avatar
Erick Hitter committed
141 142 143
	daemonURL.Scheme = protocol
	daemonURL.Host = fmt.Sprintf("%s:%d", host, port)
	daemonURL.Path = path
Erick Hitter's avatar
Erick Hitter committed
144 145

	userInfo := url.UserPassword(username, password)
Erick Hitter's avatar
Erick Hitter committed
146
	daemonURL.User = userInfo
Erick Hitter's avatar
Erick Hitter committed
147

Erick Hitter's avatar
Erick Hitter committed
148
	query := daemonURL.Query()
Erick Hitter's avatar
Erick Hitter committed
149
	query.Set("hostname", hostname)
Erick Hitter's avatar
Erick Hitter committed
150
	daemonURL.RawQuery = query.Encode()
Erick Hitter's avatar
Erick Hitter committed
151

Erick Hitter's avatar
Erick Hitter committed
152
	return daemonURL, nil
Erick Hitter's avatar
Erick Hitter committed
153 154 155
}

// Retrieve given URL
Erick Hitter's avatar
Erick Hitter committed
156
func getURL(url string) (string, error) {
Erick Hitter's avatar
Erick Hitter committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}

	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return "", err
	}

	respBodyString := string(respBody)
	return respBodyString, nil
Erick Hitter's avatar
Erick Hitter committed
171
}