Skip to content

Commit

Permalink
feat(providers): add myaddr.tools (#885)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianshea2 authored Dec 24, 2024
1 parent be66792 commit 03154c3
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
33 changes: 33 additions & 0 deletions docs/myaddr.md
Original file line number Diff line number Diff line change
@@ -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/)
11 changes: 10 additions & 1 deletion internal/params/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) (
Expand Down Expand Up @@ -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()
}
Expand Down
2 changes: 2 additions & 0 deletions internal/provider/constants/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -90,6 +91,7 @@ func ProviderChoices() []models.Provider {
Linode,
Loopia,
LuaDNS,
Myaddr,
Namecheap,
NameCom,
Njalla,
Expand Down
3 changes: 3 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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:
Expand Down
131 changes: 131 additions & 0 deletions internal/provider/providers/myaddr/provider.go
Original file line number Diff line number Diff line change
@@ -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))
}
}

0 comments on commit 03154c3

Please sign in to comment.