mirror of
https://github.com/qdm12/ddns-updater.git
synced 2026-03-31 06:24:01 -04:00
feat(providers): add myaddr.tools (#885)
This commit is contained in:
@@ -78,6 +78,7 @@ This readme and the [docs/](docs/) directory are **versioned** to match the prog
|
||||
- Linode
|
||||
- Loopia
|
||||
- LuaDNS
|
||||
- Myaddr
|
||||
- Name.com
|
||||
- Namecheap
|
||||
- Netcup
|
||||
@@ -243,6 +244,7 @@ Check the documentation for your DNS provider:
|
||||
- [Linode](docs/linode.md)
|
||||
- [Loopia](docs/loopia.md)
|
||||
- [LuaDNS](docs/luadns.md)
|
||||
- [Myaddr](docs/myaddr.md)
|
||||
- [Name.com](docs/name.com.md)
|
||||
- [Namecheap](docs/namecheap.md)
|
||||
- [Netcup](docs/netcup.md)
|
||||
|
||||
33
docs/myaddr.md
Normal file
33
docs/myaddr.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# [Myaddr](https://myaddr.tools/)
|
||||
|
||||
## Configuration
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": [
|
||||
{
|
||||
"provider": "myaddr",
|
||||
"domain": "your-name.myaddr.tools",
|
||||
"key": "key",
|
||||
"ip_version": "ipv4",
|
||||
"ipv6_suffix": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Compulsory parameters
|
||||
|
||||
- `"domain"` - the **single** domain to update; note the `key` below updates all records and subdomains for this domain. It should be `your-name*.myaddr.tools`.
|
||||
- `"key"` - the private key corresponding to the domain to update
|
||||
|
||||
### Optional parameters
|
||||
|
||||
- `"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`.
|
||||
- `"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.
|
||||
|
||||
## Domain setup
|
||||
|
||||
Claim a subdomain at [myaddr.tools](https://myaddr.tools/)
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/qdm12/ddns-updater/internal/models"
|
||||
"github.com/qdm12/ddns-updater/internal/provider"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/constants"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/utils"
|
||||
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
@@ -145,7 +146,10 @@ func extractAllSettings(jsonBytes []byte) (
|
||||
return allProviders, warnings, nil
|
||||
}
|
||||
|
||||
var ErrProviderNoLongerSupported = errors.New("provider no longer supported")
|
||||
var (
|
||||
ErrProviderNoLongerSupported = errors.New("provider no longer supported")
|
||||
ErrProviderMultipleDomains = errors.New("provider does not support multiple domains")
|
||||
)
|
||||
|
||||
func makeSettingsFromObject(common commonSettings, rawSettings json.RawMessage,
|
||||
retroGlobalIPv6Suffix netip.Prefix) (
|
||||
@@ -178,6 +182,11 @@ func makeSettingsFromObject(common commonSettings, rawSettings json.RawMessage,
|
||||
}
|
||||
}
|
||||
|
||||
if common.Provider == string(constants.Myaddr) && len(owners) > 1 {
|
||||
return nil, nil, fmt.Errorf("%w: %s for parent domain %q",
|
||||
ErrProviderMultipleDomains, common.Provider, domain)
|
||||
}
|
||||
|
||||
if common.IPVersion == "" {
|
||||
common.IPVersion = ipversion.IP4or6.String()
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ const (
|
||||
Linode models.Provider = "linode"
|
||||
Loopia models.Provider = "loopia"
|
||||
LuaDNS models.Provider = "luadns"
|
||||
Myaddr models.Provider = "myaddr"
|
||||
Namecheap models.Provider = "namecheap"
|
||||
NameCom models.Provider = "name.com"
|
||||
Netcup models.Provider = "netcup"
|
||||
@@ -90,6 +91,7 @@ func ProviderChoices() []models.Provider {
|
||||
Linode,
|
||||
Loopia,
|
||||
LuaDNS,
|
||||
Myaddr,
|
||||
Namecheap,
|
||||
NameCom,
|
||||
Njalla,
|
||||
|
||||
@@ -43,6 +43,7 @@ import (
|
||||
"github.com/qdm12/ddns-updater/internal/provider/providers/linode"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/providers/loopia"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/providers/luadns"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/providers/myaddr"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/providers/namecheap"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/providers/namecom"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/providers/netcup"
|
||||
@@ -148,6 +149,8 @@ func New(providerName models.Provider, data json.RawMessage, domain, owner strin
|
||||
return loopia.New(data, domain, owner, ipVersion, ipv6Suffix)
|
||||
case constants.LuaDNS:
|
||||
return luadns.New(data, domain, owner, ipVersion, ipv6Suffix)
|
||||
case constants.Myaddr:
|
||||
return myaddr.New(data, domain, owner, ipVersion, ipv6Suffix)
|
||||
case constants.Namecheap:
|
||||
return namecheap.New(data, domain, owner)
|
||||
case constants.NameCom:
|
||||
|
||||
131
internal/provider/providers/myaddr/provider.go
Normal file
131
internal/provider/providers/myaddr/provider.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package myaddr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/ddns-updater/internal/models"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/constants"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/errors"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/headers"
|
||||
"github.com/qdm12/ddns-updater/internal/provider/utils"
|
||||
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
domain string
|
||||
owner string
|
||||
ipVersion ipversion.IPVersion
|
||||
ipv6Suffix netip.Prefix
|
||||
key string
|
||||
}
|
||||
|
||||
func New(data json.RawMessage, domain, owner string,
|
||||
ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix,
|
||||
) (*Provider, error) {
|
||||
var providerSpecificSettings struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
err := json.Unmarshal(data, &providerSpecificSettings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json decoding provider specific settings: %w", err)
|
||||
}
|
||||
err = validateSettings(domain, providerSpecificSettings.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating provider specific settings: %w", err)
|
||||
}
|
||||
return &Provider{
|
||||
domain: domain,
|
||||
owner: owner,
|
||||
ipVersion: ipVersion,
|
||||
ipv6Suffix: ipv6Suffix,
|
||||
key: providerSpecificSettings.Key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateSettings(domain, key string) (err error) {
|
||||
err = utils.CheckDomain(domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %w", errors.ErrDomainNotValid, err)
|
||||
}
|
||||
if key == "" {
|
||||
return fmt.Errorf("%w", errors.ErrKeyNotSet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) String() string {
|
||||
return utils.ToString(p.Domain(), p.Owner(), constants.Myaddr, p.IPVersion())
|
||||
}
|
||||
|
||||
func (p *Provider) Domain() string {
|
||||
return p.domain
|
||||
}
|
||||
|
||||
func (p *Provider) Owner() string {
|
||||
return p.owner
|
||||
}
|
||||
|
||||
func (p *Provider) BuildDomainName() string {
|
||||
return utils.BuildDomainName(p.owner, p.domain)
|
||||
}
|
||||
|
||||
func (p *Provider) HTML() models.HTMLRow {
|
||||
return models.HTMLRow{
|
||||
Domain: fmt.Sprintf("<a href=\"http://%s\">%s</a>", p.BuildDomainName(), p.BuildDomainName()),
|
||||
Owner: p.Owner(),
|
||||
Provider: "<a href=\"https://myaddr.tools/\">myaddr</a>",
|
||||
IPVersion: p.IPVersion().String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) Proxied() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Provider) IPVersion() ipversion.IPVersion {
|
||||
return p.ipVersion
|
||||
}
|
||||
|
||||
func (p *Provider) IPv6Suffix() netip.Prefix {
|
||||
return p.ipv6Suffix
|
||||
}
|
||||
|
||||
func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Addr) (netip.Addr, error) {
|
||||
u := &url.URL{
|
||||
Scheme: "https",
|
||||
Host: "myaddr.tools",
|
||||
Path: "/update",
|
||||
}
|
||||
v := url.Values{}
|
||||
v.Set("key", p.key)
|
||||
v.Set("ip", ip.String())
|
||||
buffer := strings.NewReader(v.Encode())
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), buffer)
|
||||
if err != nil {
|
||||
return netip.Addr{}, fmt.Errorf("creating http request: %w", err)
|
||||
}
|
||||
headers.SetContentType(request, "application/x-www-form-urlencoded")
|
||||
headers.SetUserAgent(request)
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
switch response.StatusCode {
|
||||
case http.StatusOK:
|
||||
return ip, nil
|
||||
case http.StatusBadRequest:
|
||||
return netip.Addr{}, fmt.Errorf("%w: %s", errors.ErrBadRequest, utils.BodyToSingleLine(response.Body))
|
||||
case http.StatusNotFound:
|
||||
return netip.Addr{}, fmt.Errorf("%w: %s", errors.ErrKeyNotValid, utils.BodyToSingleLine(response.Body))
|
||||
default:
|
||||
return netip.Addr{}, fmt.Errorf("%w: %d: %s",
|
||||
errors.ErrHTTPStatusNotValid, response.StatusCode, utils.BodyToSingleLine(response.Body))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user