Skip to content

Commit 18fba12

Browse files
committed
Add IPv64 provider
1 parent 20ac110 commit 18fba12

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ Check the documentation for your DNS provider:
242242
- [Infomaniak](docs/infomaniak.md)
243243
- [INWX](docs/inwx.md)
244244
- [Ionos](docs/ionos.md)
245+
- [IPv64](docs/ipv64.md)
245246
- [Linode](docs/linode.md)
246247
- [Loopia](docs/loopia.md)
247248
- [LuaDNS](docs/luadns.md)

docs/ipv64.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# IPv64
2+
3+
## Configuration
4+
5+
### Example
6+
7+
```json
8+
{
9+
"settings": [
10+
{
11+
"provider": "ipv64",
12+
"domain": "domain.com",
13+
"key": "key",
14+
"ip_version": "ipv4",
15+
"ipv6_suffix": ""
16+
}
17+
]
18+
}
19+
```
20+
21+
### Compulsory parameters
22+
23+
- `"domain"` is the domain to update. It can be `example.com` (root domain) or `sub.example.com` (subdomain of `example.com`).
24+
- `"key"` that you can obtain [here](https://ipv64.net/account)
25+
26+
### Optional parameters
27+
28+
- `"ip_version"` can be `ipv4` (A records), or `ipv6` (AAAA records) or `ipv4 or ipv6` (update one of the two, depending on the public ip found). It defaults to `ipv4 or ipv6`.
29+
- `"ipv6_suffix"` is the IPv6 interface identifier suffix to use. It can be for example `0:0:0:0:72ad:8fbb:a54e:bedd/64`. If left empty, it defaults to no suffix and the raw public IPv6 address obtained is used in the record updating.
30+
31+
## Domain setup

internal/provider/constants/providers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const (
3434
Infomaniak models.Provider = "infomaniak"
3535
INWX models.Provider = "inwx"
3636
Ionos models.Provider = "ionos"
37+
IPv64 models.Provider = "ipv64"
3738
Linode models.Provider = "linode"
3839
Loopia models.Provider = "loopia"
3940
LuaDNS models.Provider = "luadns"
@@ -89,6 +90,7 @@ func ProviderChoices() []models.Provider {
8990
Infomaniak,
9091
INWX,
9192
Ionos,
93+
IPv64,
9294
Linode,
9395
Loopia,
9496
LuaDNS,

internal/provider/provider.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
"github.com/qdm12/ddns-updater/internal/provider/providers/infomaniak"
4141
"github.com/qdm12/ddns-updater/internal/provider/providers/inwx"
4242
"github.com/qdm12/ddns-updater/internal/provider/providers/ionos"
43+
"github.com/qdm12/ddns-updater/internal/provider/providers/ipv64"
4344
"github.com/qdm12/ddns-updater/internal/provider/providers/linode"
4445
"github.com/qdm12/ddns-updater/internal/provider/providers/loopia"
4546
"github.com/qdm12/ddns-updater/internal/provider/providers/luadns"
@@ -144,6 +145,8 @@ func New(providerName models.Provider, data json.RawMessage, domain, owner strin
144145
return inwx.New(data, domain, owner, ipVersion, ipv6Suffix)
145146
case constants.Ionos:
146147
return ionos.New(data, domain, owner, ipVersion, ipv6Suffix)
148+
case constants.IPv64:
149+
return ipv64.New(data, domain, owner, ipVersion, ipv6Suffix)
147150
case constants.Linode:
148151
return linode.New(data, domain, owner, ipVersion, ipv6Suffix)
149152
case constants.Loopia:
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package ipv64
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"net/netip"
9+
"net/url"
10+
11+
"github.com/qdm12/ddns-updater/internal/models"
12+
"github.com/qdm12/ddns-updater/internal/provider/constants"
13+
"github.com/qdm12/ddns-updater/internal/provider/errors"
14+
"github.com/qdm12/ddns-updater/internal/provider/headers"
15+
"github.com/qdm12/ddns-updater/internal/provider/utils"
16+
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
17+
)
18+
19+
type Provider struct {
20+
domain string
21+
owner string
22+
ipVersion ipversion.IPVersion
23+
ipv6Suffix netip.Prefix
24+
key string
25+
}
26+
27+
func New(data json.RawMessage, domain, owner string,
28+
ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
29+
p *Provider, err error,
30+
) {
31+
extraSettings := struct {
32+
Key string `json:"key"`
33+
}{}
34+
err = json.Unmarshal(data, &extraSettings)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
err = validateSettings(domain, extraSettings.Key)
40+
if err != nil {
41+
return nil, fmt.Errorf("validating provider specific settings: %w", err)
42+
}
43+
44+
return &Provider{
45+
domain: domain,
46+
owner: owner,
47+
ipVersion: ipVersion,
48+
ipv6Suffix: ipv6Suffix,
49+
key: extraSettings.Key,
50+
}, nil
51+
}
52+
53+
func validateSettings(domain, token string) (err error) {
54+
err = utils.CheckDomain(domain)
55+
if err != nil {
56+
return fmt.Errorf("%w: %w", errors.ErrDomainNotValid, err)
57+
}
58+
59+
if token == "" {
60+
return fmt.Errorf("%w", errors.ErrTokenNotSet)
61+
}
62+
return nil
63+
}
64+
65+
func (p *Provider) String() string {
66+
return utils.ToString(p.domain, p.owner, constants.IPv64, p.ipVersion)
67+
}
68+
69+
func (p *Provider) Domain() string {
70+
return p.domain
71+
}
72+
73+
func (p *Provider) Owner() string {
74+
return p.owner
75+
}
76+
77+
func (p *Provider) IPVersion() ipversion.IPVersion {
78+
return p.ipVersion
79+
}
80+
81+
func (p *Provider) IPv6Suffix() netip.Prefix {
82+
return p.ipv6Suffix
83+
}
84+
85+
func (p *Provider) Proxied() bool {
86+
return false
87+
}
88+
89+
func (p *Provider) BuildDomainName() string {
90+
return utils.BuildDomainName(p.owner, p.domain)
91+
}
92+
93+
func (p *Provider) HTML() models.HTMLRow {
94+
return models.HTMLRow{
95+
Domain: fmt.Sprintf("<a href=\"http://%s\">%s</a>", p.BuildDomainName(), p.BuildDomainName()),
96+
Owner: p.Owner(),
97+
Provider: "<a href=\"https://ipv64.net/\">IPv64 DNS</a>",
98+
IPVersion: p.ipVersion.String(),
99+
}
100+
}
101+
102+
// see https://ipv64.net/dyndns_updater_api
103+
func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Addr) (newIP netip.Addr, err error) {
104+
u := url.URL{
105+
Scheme: "https",
106+
Host: "ipv64.net",
107+
Path: "/nic/update",
108+
}
109+
110+
values := url.Values{}
111+
values.Set("key", p.key)
112+
values.Set("domain", utils.BuildURLQueryHostname(p.owner, p.domain))
113+
114+
if ip.Is4() {
115+
values.Set("ip", ip.String())
116+
} else {
117+
values.Set("ip6", ip.String())
118+
}
119+
120+
u.RawQuery = values.Encode()
121+
122+
request, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
123+
if err != nil {
124+
return netip.Addr{}, fmt.Errorf("creating http request: %w", err)
125+
}
126+
headers.SetUserAgent(request)
127+
128+
response, err := client.Do(request)
129+
if err != nil {
130+
return netip.Addr{}, err
131+
}
132+
defer response.Body.Close()
133+
134+
if response.StatusCode == http.StatusOK {
135+
return ip, nil
136+
}
137+
return netip.Addr{}, fmt.Errorf("%w: %d: %s",
138+
errors.ErrHTTPStatusNotValid, response.StatusCode, utils.BodyToSingleLine(response.Body))
139+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy