mirror of
https://github.com/qdm12/ddns-updater.git
synced 2026-08-04 19:35:08 -04:00
Google domain names support (#70)
This commit is contained in:
1
.github/ISSUE_TEMPLATE/bug.md
vendored
1
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -22,6 +22,7 @@ assignees: qdm12
|
||||
- [ ] Dreamhost
|
||||
- [ ] DuckDNS
|
||||
- [ ] GoDaddy
|
||||
- [ ] Google
|
||||
- [ ] Infomaniak
|
||||
- [ ] Namecheap
|
||||
- [ ] NoIP
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/help.md
vendored
1
.github/ISSUE_TEMPLATE/help.md
vendored
@@ -22,6 +22,7 @@ assignees:
|
||||
- [ ] Dreamhost
|
||||
- [ ] DuckDNS
|
||||
- [ ] GoDaddy
|
||||
- [ ] Google
|
||||
- [ ] Infomaniak
|
||||
- [ ] Namecheap
|
||||
- [ ] NoIP
|
||||
|
||||
@@ -32,7 +32,7 @@ LABEL \
|
||||
org.opencontainers.image.documentation="https://github.com/qdm12/ddns-updater" \
|
||||
org.opencontainers.image.source="https://github.com/qdm12/ddns-updater" \
|
||||
org.opencontainers.image.title="ddns-updater" \
|
||||
org.opencontainers.image.description="Universal DNS updater with WebUI. Works with Cloudflare, DDNSS.de, DNSPod, Dreamhost, DuckDNS, GoDaddy, Infomaniak, Namecheap and NoIP"
|
||||
org.opencontainers.image.description="Universal DNS updater with WebUI. Works with Cloudflare, DDNSS.de, DNSPod, Dreamhost, DuckDNS, GoDaddy, Google, Infomaniak, Namecheap and NoIP"
|
||||
COPY --from=alpine --chown=1000 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=alpine --chown=1000 /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
EXPOSE 8000
|
||||
|
||||
14
README.md
14
README.md
@@ -1,6 +1,6 @@
|
||||
# Lightweight universal DDNS Updater with Docker and web UI
|
||||
|
||||
*Light container updating DNS A records periodically for Cloudflare, DDNSS.de, DNSPod, Dreamhost, DuckDNS, GoDaddy, Infomaniak, Namecheap and NoIP*
|
||||
*Light container updating DNS A records periodically for Cloudflare, DDNSS.de, DNSPod, Dreamhost, DuckDNS, GoDaddy, Google, Infomaniak, Namecheap and NoIP*
|
||||
|
||||
[](https://hub.docker.com/r/qmcgaw/ddns-updater)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
## Features
|
||||
|
||||
- Updates periodically A records for different DNS providers: Cloudflare, DDNSS.de, DNSPod, Dreamhost, DuckDNS, GoDaddy, Infomaniak, Namecheap and NoIP ([create an issue](https://github.com/qdm12/ddns-updater/issues/new/choose) for more)
|
||||
- Updates periodically A records for different DNS providers: Cloudflare, DDNSS.de, DNSPod, Dreamhost, DuckDNS, GoDaddy, Google, Infomaniak, Namecheap and NoIP ([create an issue](https://github.com/qdm12/ddns-updater/issues/new/choose) for more)
|
||||
- Web User interface
|
||||
|
||||

|
||||
@@ -128,7 +128,7 @@ Start by having the following content in *config.json*, or in your `CONFIG` envi
|
||||
|
||||
The following parameters are to be added:
|
||||
|
||||
For all record update configuration, you have to specify the DNS provider with `"provider"` which can be `"cloudflare"`, `"ddnss"`, `"dnspod"`, `"dreamhost"`, `"duckdns"`, `"godaddy"`, `"infomaniak"`, `"namecheap"` or `"noip"`.
|
||||
For all record update configuration, you have to specify the DNS provider with `"provider"` which can be `"cloudflare"`, `"ddnss"`, `"dnspod"`, `"dreamhost"`, `"duckdns"`, `"godaddy"`, `"google"`, `"infomaniak"`, `"namecheap"` or `"noip"`.
|
||||
You can optionnally add the parameters:
|
||||
|
||||
- `"no_dns_lookup"` can be `true` or `false` and allows, if `true`, to prevent the periodic Docker healthcheck from running a DNS lookup on your domain.
|
||||
@@ -201,6 +201,14 @@ DDNSS.de:
|
||||
- `"password"`
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
|
||||
Google:
|
||||
|
||||
- `"domain"`
|
||||
- `"host"` is your host and can be a subdomain or `"@"` or `"*"`
|
||||
- `"username"`
|
||||
- `"password"`
|
||||
- `"ip_version"` can be `ipv4` (A records) or `ipv6` (AAAA records), defaults to `ipv4 or ipv6`
|
||||
|
||||
### Additional notes
|
||||
|
||||
- You can specify multiple hosts for the same domain using a comma separated list. For example with `"host": "@,subdomain1,subdomain2",`.
|
||||
|
||||
@@ -10,6 +10,7 @@ const (
|
||||
DUCKDNS models.Provider = "duckdns"
|
||||
DREAMHOST models.Provider = "dreamhost"
|
||||
GODADDY models.Provider = "godaddy"
|
||||
GOOGLE models.Provider = "google"
|
||||
INFOMANIAK models.Provider = "infomaniak"
|
||||
NAMECHEAP models.Provider = "namecheap"
|
||||
NOIP models.Provider = "noip"
|
||||
@@ -23,6 +24,7 @@ func ProviderChoices() []models.Provider {
|
||||
DUCKDNS,
|
||||
DREAMHOST,
|
||||
GODADDY,
|
||||
GOOGLE,
|
||||
INFOMANIAK,
|
||||
NAMECHEAP,
|
||||
NOIP,
|
||||
|
||||
@@ -121,6 +121,8 @@ func makeSettingsFromObject(common commonSettings, rawSettings json.RawMessage)
|
||||
settingsConstructor = settings.NewDuckdns
|
||||
case constants.GODADDY:
|
||||
settingsConstructor = settings.NewGodaddy
|
||||
case constants.GOOGLE:
|
||||
settingsConstructor = settings.NewGoogle
|
||||
case constants.INFOMANIAK:
|
||||
settingsConstructor = settings.NewInfomaniak
|
||||
case constants.NAMECHEAP:
|
||||
|
||||
7
internal/settings/constants.go
Normal file
7
internal/settings/constants.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package settings
|
||||
|
||||
const (
|
||||
badauth = "badauth"
|
||||
success = "success"
|
||||
nohost = "nohost"
|
||||
)
|
||||
@@ -130,7 +130,7 @@ func (d *ddnss) Update(client network.Client, ip net.IP) (newIP net.IP, err erro
|
||||
switch {
|
||||
case strings.Contains(s, "badysys"):
|
||||
return nil, fmt.Errorf("ddnss.de: invalid system parameter")
|
||||
case strings.Contains(s, "badauth"):
|
||||
case strings.Contains(s, badauth):
|
||||
return nil, fmt.Errorf("ddnss.de: bad authentication")
|
||||
case strings.Contains(s, "notfqdn"):
|
||||
return nil, fmt.Errorf("ddnss.de: hostname %q does not exist", fqdn)
|
||||
|
||||
@@ -87,8 +87,6 @@ func (d *dreamhost) HTML() models.HTMLRow {
|
||||
}
|
||||
}
|
||||
|
||||
const success = "success"
|
||||
|
||||
func (d *dreamhost) Update(client network.Client, ip net.IP) (newIP net.IP, err error) {
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("IP address was not given to updater")
|
||||
|
||||
155
internal/settings/google.go
Normal file
155
internal/settings/google.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/ddns-updater/internal/models"
|
||||
netlib "github.com/qdm12/golibs/network"
|
||||
"github.com/qdm12/golibs/verification"
|
||||
)
|
||||
|
||||
//nolint:maligned
|
||||
type google struct {
|
||||
domain string
|
||||
host string
|
||||
ipVersion models.IPVersion
|
||||
dnsLookup bool
|
||||
username string
|
||||
password string
|
||||
useProviderIP bool
|
||||
}
|
||||
|
||||
func NewGoogle(data json.RawMessage, domain, host string, ipVersion models.IPVersion, noDNSLookup bool) (s Settings, err error) {
|
||||
extraSettings := struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
UseProviderIP bool `json:"provider_ip"`
|
||||
}{}
|
||||
if err := json.Unmarshal(data, &extraSettings); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g := &google{
|
||||
domain: domain,
|
||||
host: host,
|
||||
ipVersion: ipVersion,
|
||||
dnsLookup: !noDNSLookup,
|
||||
username: extraSettings.Username,
|
||||
password: extraSettings.Password,
|
||||
useProviderIP: extraSettings.UseProviderIP,
|
||||
}
|
||||
if err := g.isValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (g *google) isValid() error {
|
||||
switch {
|
||||
case len(g.username) == 0:
|
||||
return fmt.Errorf("username cannot be empty")
|
||||
case len(g.password) == 0:
|
||||
return fmt.Errorf("password cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *google) String() string {
|
||||
return fmt.Sprintf("[domain: %s | host: %s | provider: Google]", g.domain, g.host)
|
||||
}
|
||||
|
||||
func (g *google) Domain() string {
|
||||
return g.domain
|
||||
}
|
||||
|
||||
func (g *google) Host() string {
|
||||
return g.host
|
||||
}
|
||||
|
||||
func (g *google) DNSLookup() bool {
|
||||
return g.dnsLookup
|
||||
}
|
||||
|
||||
func (g *google) IPVersion() models.IPVersion {
|
||||
return g.ipVersion
|
||||
}
|
||||
|
||||
func (g *google) BuildDomainName() string {
|
||||
return buildDomainName(g.host, g.domain)
|
||||
}
|
||||
|
||||
func (g *google) HTML() models.HTMLRow {
|
||||
return models.HTMLRow{
|
||||
Domain: models.HTML(fmt.Sprintf("<a href=\"http://%s\">%s</a>", g.BuildDomainName(), g.BuildDomainName())),
|
||||
Host: models.HTML(g.Host()),
|
||||
Provider: "<a href=\"https://domains.google.com/m/registrar\">Google</a>",
|
||||
IPVersion: models.HTML(g.ipVersion),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *google) Update(client netlib.Client, ip net.IP) (newIP net.IP, err error) {
|
||||
u := url.URL{
|
||||
Scheme: "https",
|
||||
Host: "domains.google.com",
|
||||
Path: "/nic/update",
|
||||
User: url.UserPassword(g.username, g.password),
|
||||
}
|
||||
values := url.Values{}
|
||||
fqdn := g.BuildDomainName()
|
||||
values.Set("hostname", fqdn)
|
||||
if !g.useProviderIP {
|
||||
values.Set("myip", ip.String())
|
||||
}
|
||||
u.RawQuery = values.Encode()
|
||||
r, err := http.NewRequest(http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Header.Set("User-Agent", "DDNS-Updater quentig.mcgaw@gmail.com")
|
||||
status, content, err := client.DoHTTPRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := string(content)
|
||||
switch s {
|
||||
case "":
|
||||
return nil, fmt.Errorf("HTTP status %d", status)
|
||||
case nohost:
|
||||
return nil, fmt.Errorf("hostname does not exist")
|
||||
case badauth:
|
||||
return nil, fmt.Errorf("invalid username password combination")
|
||||
case "notfqdn":
|
||||
return nil, fmt.Errorf("hostname %q is not a valid fully qualified domain name", fqdn)
|
||||
case "badagent":
|
||||
return nil, fmt.Errorf("user agent is banned")
|
||||
case "abuse":
|
||||
return nil, fmt.Errorf("username is banned due to abuse")
|
||||
case "911":
|
||||
return nil, fmt.Errorf("Google's internal server error 911")
|
||||
case "conflict A":
|
||||
return nil, fmt.Errorf("custom A record conflicts with the update")
|
||||
case "conflict AAAA":
|
||||
return nil, fmt.Errorf("custom AAAA record conflicts with the update")
|
||||
}
|
||||
if strings.Contains(s, "nochg") || strings.Contains(s, "good") {
|
||||
ipsV4 := verification.NewVerifier().SearchIPv4(s)
|
||||
ipsV6 := verification.NewVerifier().SearchIPv6(s)
|
||||
ips := append(ipsV4, ipsV6...)
|
||||
if ips == nil {
|
||||
return nil, fmt.Errorf("no IP address in response")
|
||||
}
|
||||
newIP = net.ParseIP(ips[0])
|
||||
if newIP == nil {
|
||||
return nil, fmt.Errorf("IP address received %q is malformed", ips[0])
|
||||
}
|
||||
if !g.useProviderIP && !ip.Equal(newIP) {
|
||||
return nil, fmt.Errorf("new IP address %s is not %s", newIP.String(), ip.String())
|
||||
}
|
||||
return newIP, nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid response %q", s)
|
||||
}
|
||||
@@ -142,9 +142,9 @@ func (i *infomaniak) Update(client network.Client, ip net.IP) (newIP net.IP, err
|
||||
}
|
||||
case http.StatusBadRequest:
|
||||
switch s {
|
||||
case "nohost":
|
||||
case nohost:
|
||||
return nil, fmt.Errorf("infomaniak.com: host %q does not exist for domain %q", i.host, i.domain)
|
||||
case "badauth":
|
||||
case badauth:
|
||||
return nil, fmt.Errorf("infomaniak.com: bad authentication")
|
||||
default:
|
||||
return nil, fmt.Errorf("infomaniak.com: bad request: %s", s)
|
||||
|
||||
@@ -129,9 +129,9 @@ func (n *noip) Update(client netlib.Client, ip net.IP) (newIP net.IP, err error)
|
||||
return nil, fmt.Errorf("user has not this extra feature")
|
||||
case "badagent":
|
||||
return nil, fmt.Errorf("user agent is banned")
|
||||
case "badauth":
|
||||
case badauth:
|
||||
return nil, fmt.Errorf("invalid username password combination")
|
||||
case "nohost":
|
||||
case nohost:
|
||||
return nil, fmt.Errorf("hostname does not exist")
|
||||
}
|
||||
if strings.Contains(s, "nochg") || strings.Contains(s, "good") {
|
||||
@@ -143,7 +143,7 @@ func (n *noip) Update(client netlib.Client, ip net.IP) (newIP net.IP, err error)
|
||||
if newIP == nil {
|
||||
return nil, fmt.Errorf("IP address received %q is malformed", ips[0])
|
||||
}
|
||||
if ip != nil && !ip.Equal(newIP) {
|
||||
if !n.useProviderIP && !ip.Equal(newIP) {
|
||||
return nil, fmt.Errorf("new IP address %s is not %s", newIP.String(), ip.String())
|
||||
}
|
||||
return newIP, nil
|
||||
|
||||
Reference in New Issue
Block a user