fix(ipv6): add JSON IPv6 suffix parameter (#611)

- Remove `IPV6_PREFIX` environment variable (unneeded) and remove associated code
- Update all documentation for each provider supporting IPv6
- Build IPv6 as prefix:suffix when getting it from a public IP source for each record IPv6 suffix parameter
- Automatically disable provider_ip if public ip is IPv6 and IPv6 suffix is set (they are not compatible with each other)
This commit is contained in:
Quentin McGaw
2024-01-29 17:31:07 +01:00
committed by GitHub
parent 25b8e02acc
commit bad0d3aeda
98 changed files with 806 additions and 498 deletions

View File

@@ -73,7 +73,6 @@ ENV \
# Core # Core
CONFIG= \ CONFIG= \
PERIOD=5m \ PERIOD=5m \
IPV6_PREFIX=/128 \
UPDATE_COOLDOWN_PERIOD=5m \ UPDATE_COOLDOWN_PERIOD=5m \
PUBLICIP_FETCHERS=all \ PUBLICIP_FETCHERS=all \
PUBLICIP_HTTP_PROVIDERS=all \ PUBLICIP_HTTP_PROVIDERS=all \

View File

@@ -127,7 +127,6 @@ The program reads the configuration from a JSON object, either from a file or fr
docker run -d -p 8000:8000/tcp -v "$(pwd)"/data:/updater/data qmcgaw/ddns-updater docker run -d -p 8000:8000/tcp -v "$(pwd)"/data:/updater/data qmcgaw/ddns-updater
``` ```
1. ⚠️ If you use IPv6, you might need to set `-e IPV6_PREFIX=/64` (`/64` is your prefix, depending on your ISP)
1. (Optional) You can also set your JSON configuration as a single environment variable line (i.e. `{"settings": [{"provider": "namecheap", ...}]}`), which takes precedence over config.json. Note however that if you don't bind mount the `/updater/data` directory, there won't be a persistent database file `/updater/updates.json` but it will still work. 1. (Optional) You can also set your JSON configuration as a single environment variable line (i.e. `{"settings": [{"provider": "namecheap", ...}]}`), which takes precedence over config.json. Note however that if you don't bind mount the `/updater/data` directory, there won't be a persistent database file `/updater/updates.json` but it will still work.
### Next steps ### Next steps
@@ -224,7 +223,6 @@ Note that:
| --- | --- | --- | | --- | --- | --- |
| `CONFIG` | | One line JSON object containing the entire config (takes precedence over config.json file) if specified | | `CONFIG` | | One line JSON object containing the entire config (takes precedence over config.json file) if specified |
| `PERIOD` | `5m` | Default period of IP address check, following [this format](https://golang.org/pkg/time/#ParseDuration) | | `PERIOD` | `5m` | Default period of IP address check, following [this format](https://golang.org/pkg/time/#ParseDuration) |
| `IPV6_PREFIX` | `/128` | IPv6 prefix used to mask your public IPv6 address and your record IPv6 address. Ranges from `/0` to `/128` depending on your ISP. |
| `PUBLICIP_FETCHERS` | `all` | Comma separated fetcher types to obtain the public IP address from `http` and `dns` | | `PUBLICIP_FETCHERS` | `all` | Comma separated fetcher types to obtain the public IP address from `http` and `dns` |
| `PUBLICIP_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IP address (ipv4 or ipv6). See the [Public IP section](#public-ip) | | `PUBLICIP_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IP address (ipv4 or ipv6). See the [Public IP section](#public-ip) |
| `PUBLICIPV4_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IPv4 address only. See the [Public IP section](#public-ip) | | `PUBLICIPV4_HTTP_PROVIDERS` | `all` | Comma separated providers to obtain the public IPv4 address only. See the [Public IP section](#public-ip) |

View File

@@ -8,7 +8,6 @@ import (
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings"
"syscall" "syscall"
"time" "time"
_ "time/tzdata" _ "time/tzdata"
@@ -248,8 +247,7 @@ func _main(ctx context.Context, reader *reader.Reader, args []string, logger log
updater := update.NewUpdater(db, client, shoutrrrClient, logger) updater := update.NewUpdater(db, client, shoutrrrClient, logger)
runner := update.NewRunner(db, updater, ipGetter, config.Update.Period, runner := update.NewRunner(db, updater, ipGetter, config.Update.Period,
ipv6PrefixToBits(config.IPv6.Prefix), config.Update.Cooldown, logger, config.Update.Cooldown, logger, resolver, timeNow, hioClient)
resolver, timeNow, hioClient)
runnerHandler, runnerCtx, runnerDone := goshutdown.NewGoRoutineHandler("runner") runnerHandler, runnerCtx, runnerDone := goshutdown.NewGoRoutineHandler("runner")
go runner.Run(runnerCtx, runnerDone) go runner.Run(runnerCtx, runnerDone)
@@ -326,12 +324,3 @@ func backupRunLoop(ctx context.Context, done chan<- struct{}, backupPeriod time.
} }
} }
} }
func ipv6PrefixToBits(prefix string) (maskBits uint8) {
prefix = strings.TrimPrefix(prefix, "/")
n, err := strconv.Atoi(prefix)
if err != nil {
panic(err) // prefix already validated before
}
return uint8(n)
}

View File

@@ -13,7 +13,8 @@
"host": "@", "host": "@",
"access_key_id": "your access_key_id", "access_key_id": "your access_key_id",
"access_secret": "your access_secret", "access_secret": "your access_secret",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -29,5 +30,6 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -13,7 +13,8 @@
"host": "host", "host": "host",
"username": "dynXXXXXXX", "username": "dynXXXXXXX",
"password": "password", "password": "password",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -29,5 +30,6 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -14,7 +14,8 @@
"host": "@", "host": "@",
"ttl": 600, "ttl": 600,
"token": "yourtoken", "token": "yourtoken",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -36,5 +37,6 @@ See [this issue comment for context](https://github.com/qdm12/ddns-updater/issue
- `"proxied"` can be set to `true` to use the proxy services of Cloudflare - `"proxied"` can be set to `true` to use the proxy services of Cloudflare
- `"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`. - `"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 indentifier 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.
Special thanks to @Starttoaster for helping out with the [documentation](https://gist.github.com/Starttoaster/07d568c2a99ad7631dd776688c988326) and testing. Special thanks to @Starttoaster for helping out with the [documentation](https://gist.github.com/Starttoaster/07d568c2a99ad7631dd776688c988326) and testing.

View File

@@ -20,7 +20,8 @@ Feel free to open issues to extend its configuration options.
"ipv4key": "ipv4", "ipv4key": "ipv4",
"ipv6key": "ipv6", "ipv6key": "ipv6",
"success_regex": "good", "success_regex": "good",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -38,3 +39,4 @@ Feel free to open issues to extend its configuration options.
### Optional parameters ### 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`. - `"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 indentifier 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.

View File

@@ -12,7 +12,8 @@
"domain": "domain.com", "domain": "domain.com",
"host": "@", "host": "@",
"password": "password", "password": "password",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -27,3 +28,4 @@
### Optional parameters ### 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`. - `"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 indentifier 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.

View File

@@ -15,7 +15,8 @@
"username": "user", "username": "user",
"password": "password", "password": "password",
"dual_stack": false, "dual_stack": false,
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -34,5 +35,6 @@
- if it is `false`, the updates are done using the `ip` parameter and only one IP address can be set (ipv4 or ipv6, whichever is last sent). - if it is `false`, the updates are done using the `ip` parameter and only one IP address can be set (ipv4 or ipv6, whichever is last sent).
- if it is `true`, the updates are done using the `ip` and `ip6` parameters, for IPv4 and IPv6 respectively, and both can be set on the same record - if it is `true`, the updates are done using the `ip` and `ip6` parameters, for IPv4 and IPv6 respectively, and both can be set on the same record
- `"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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -13,6 +13,7 @@
"host": "host", "host": "host",
"token": "token", "token": "token",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": false "provider_ip": false
} }
] ]
@@ -28,6 +29,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -12,7 +12,8 @@
"domain": "domain.com", "domain": "domain.com",
"host": "@", "host": "@",
"token": "yourtoken", "token": "yourtoken",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -27,5 +28,6 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -14,7 +14,8 @@
"username": "username", "username": "username",
"password": "password", "password": "password",
"provider_ip": true, "provider_ip": true,
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -31,5 +32,6 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -12,7 +12,8 @@
"domain": "domain.com", "domain": "domain.com",
"host": "@", "host": "@",
"token": "yourtoken", "token": "yourtoken",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -27,5 +28,6 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -14,7 +14,8 @@
"name": "something", "name": "something",
"username": "username", "username": "username",
"key": "key", "key": "key",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -31,6 +32,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -12,7 +12,8 @@
"domain": "domain.com", "domain": "domain.com",
"host": "@", "host": "@",
"key": "key", "key": "key",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -27,5 +28,6 @@
- `"host"` is your host and can be a subdomain or `"@"`. It defaults to `"@"`. - `"host"` is your host and can be a subdomain or `"@"`. It defaults to `"@"`.
- `"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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -12,6 +12,7 @@
"host": "host", "host": "host",
"token": "token", "token": "token",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -26,6 +27,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (**NOT** your IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (**NOT** your IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -13,7 +13,8 @@
"host": "@", "host": "@",
"username": "username", "username": "username",
"client_key": "client_key", "client_key": "client_key",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -29,5 +30,6 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -15,6 +15,7 @@
"username": "username", "username": "username",
"password": "password", "password": "password",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -31,6 +32,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
- `"group"` specify the Group for which you want to set the IP (will update any domains and subdomains in the same group) - `"group"` specify the Group for which you want to set the IP (will update any domains and subdomains in the same group)

View File

@@ -13,6 +13,7 @@
"host": "@", "host": "@",
"token": "token", "token": "token",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -28,6 +29,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -14,6 +14,7 @@
"username": "username", "username": "username",
"token": "token", "token": "token",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -30,6 +31,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -12,7 +12,8 @@
"domain": "domain.com", "domain": "domain.com",
"host": "host", "host": "host",
"token": "token", "token": "token",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -27,6 +28,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -15,7 +15,8 @@ This provider uses Gandi v5 API
"host": "@", "host": "@",
"personal_access_token": "token", "personal_access_token": "token",
"ttl": 3600, "ttl": 3600,
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -30,6 +31,7 @@ This provider uses Gandi v5 API
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"ttl"` default is `3600` - `"ttl"` default is `3600`
## Domain setup ## Domain setup

View File

@@ -18,7 +18,8 @@
}, },
"domain": "domain.com", "domain": "domain.com",
"host": "@", "host": "@",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -35,3 +36,4 @@
### Optional parameters ### 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`. - `"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 indentifier 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.

View File

@@ -13,7 +13,8 @@
"host": "@", "host": "@",
"key": "key", "key": "key",
"secret": "secret", "secret": "secret",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -29,6 +30,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -13,7 +13,8 @@
"host": "@", "host": "@",
"password": "password", "password": "password",
"provider_ip": true, "provider_ip": true,
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -28,6 +29,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -14,7 +14,8 @@
"host": "@", "host": "@",
"ttl": 600, "ttl": 600,
"token": "yourtoken", "token": "yourtoken",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -32,3 +33,4 @@
### Optional parameters ### 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`. - `"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 indentifier 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.

View File

@@ -14,6 +14,7 @@
"username": "username", "username": "username",
"password": "password", "password": "password",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -30,6 +31,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -13,7 +13,8 @@
"host": "@", "host": "@",
"username": "username", "username": "username",
"password": "password", "password": "password",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -29,5 +30,6 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -12,7 +12,8 @@
"domain": "domain.com", "domain": "domain.com",
"host": "@", "host": "@",
"api_key": "api_key", "api_key": "api_key",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -27,3 +28,4 @@
### Optional parameters ### 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`. - `"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 indentifier 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.

View File

@@ -12,7 +12,8 @@
"domain": "domain.com", "domain": "domain.com",
"host": "@", "host": "@",
"token": "token", "token": "token",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -27,6 +28,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -13,7 +13,8 @@
"host": "@", "host": "@",
"email": "email", "email": "email",
"token": "token", "token": "token",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -29,6 +30,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -16,7 +16,8 @@
"username": "username", "username": "username",
"token": "token", "token": "token",
"ttl": 300, "ttl": 300,
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -33,3 +34,4 @@
- `"ttl"` is the time this record can be cached for in seconds. Name.com allows a minimum TTL of 300, or 5 minutes. Name.com defaults to 300 if not provided. - `"ttl"` is the time this record can be cached for in seconds. Name.com allows a minimum TTL of 300, or 5 minutes. Name.com defaults to 300 if not provided.
- `"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`. - `"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 indentifier 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.

View File

@@ -18,7 +18,8 @@ Also keep in mind, that TTL, Expire, Retry and Refresh values of the given Domai
"api_key": "xxxxx", "api_key": "xxxxx",
"password": "yyyyy", "password": "yyyyy",
"customer_number": "111111", "customer_number": "111111",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -35,3 +36,4 @@ Also keep in mind, that TTL, Expire, Retry and Refresh values of the given Domai
### Optional parameters ### 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`. - `"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 indentifier 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.

View File

@@ -13,6 +13,7 @@
"host": "@", "host": "@",
"key": "key", "key": "key",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -28,6 +29,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -14,6 +14,7 @@
"username": "username", "username": "username",
"password": "password", "password": "password",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -30,6 +31,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -12,7 +12,8 @@
"domain": "domain.com", "domain": "domain.com",
"username": "username", "username": "username",
"password": "password", "password": "password",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -27,3 +28,4 @@
### Optional parameters ### 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`. - `"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 indentifier 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.

View File

@@ -14,6 +14,7 @@
"username": "username", "username": "username",
"password": "password", "password": "password",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -30,6 +31,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -14,6 +14,7 @@
"username": "username", "username": "username",
"password": "password", "password": "password",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -42,6 +43,7 @@ The ZoneDNS implementation allows you to update any record name including *.your
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
- `"mode"` select between two modes, OVH's dynamic hosting service (`"dynamic"`) or OVH's API (`"api"`). Default is `"dynamic"` - `"mode"` select between two modes, OVH's dynamic hosting service (`"dynamic"`) or OVH's API (`"api"`). Default is `"dynamic"`

View File

@@ -13,7 +13,8 @@
"host": "@", "host": "@",
"api_key": "sk1_7d119e3f656b00ae042980302e1425a04163c476efec1833q3cb0w54fc6f5022", "api_key": "sk1_7d119e3f656b00ae042980302e1425a04163c476efec1833q3cb0w54fc6f5022",
"secret_api_key": "pk1_5299b57125c8f3cdf347d2fe0e713311ee3a1e11f11a14942b26472593e35368", "secret_api_key": "pk1_5299b57125c8f3cdf347d2fe0e713311ee3a1e11f11a14942b26472593e35368",
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -30,6 +31,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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 ## Domain setup

View File

@@ -14,7 +14,8 @@
"username": "username", "username": "username",
"password": "password", "password": "password",
"provider_ip": true, "provider_ip": true,
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -30,6 +31,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -15,7 +15,8 @@
"password": "servercow_password", "password": "servercow_password",
"ttl": 600, "ttl": 600,
"provider_ip": true, "provider_ip": true,
"ip_version": "ipv4" "ip_version": "ipv4",
"ipv6_suffix": ""
} }
] ]
} }
@@ -32,6 +33,7 @@
- `"ttl"` can be set to an integer value for record TTL in seconds (if not set the default is 120) - `"ttl"` can be set to an integer value for record TTL in seconds (if not set the default is 120)
- `"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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -15,6 +15,7 @@
"password": "password", "password": "password",
"token": "token", "token": "token",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -38,4 +39,5 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (**not IPv6**)automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (**not IPv6**)automatically when you send an update request, without sending the new IP address detected by the program in the request.

View File

@@ -13,6 +13,7 @@
"host": "@", "host": "@",
"password": "password", "password": "password",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -28,6 +29,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -14,6 +14,7 @@
"email": "email@domain.com", "email": "email@domain.com",
"password": "password", "password": "password",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -30,6 +31,7 @@
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -20,6 +20,7 @@ set the environment variable as `PERIOD=11m` to check your public IP address and
"username": "username", "username": "username",
"token": "token", "token": "token",
"ip_version": "ipv4", "ip_version": "ipv4",
"ipv6_suffix": "",
"provider_ip": true "provider_ip": true
} }
] ]
@@ -36,6 +37,7 @@ set the environment variable as `PERIOD=11m` to check your public IP address and
### Optional parameters ### 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`. - `"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 indentifier 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.
- `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request. - `"provider_ip"` can be set to `true` to let your DNS provider determine your IPv4 address (and/or IPv6 address) automatically when you send an update request, without sending the new IP address detected by the program in the request.
## Domain setup ## Domain setup

View File

@@ -1,66 +0,0 @@
package config
import (
"errors"
"fmt"
"strconv"
"strings"
"github.com/qdm12/gosettings"
"github.com/qdm12/gosettings/reader"
"github.com/qdm12/gotree"
)
type IPv6 struct {
// Prefix is the IPv6 mask, for example /128
Prefix string
}
func (i *IPv6) setDefaults() {
i.Prefix = gosettings.DefaultComparable(i.Prefix, "/128")
}
func (i IPv6) Validate() (err error) {
err = validateIPv6Prefix(i.Prefix)
if err != nil {
return err
}
return nil
}
var (
ErrIPv6PrefixFormat = errors.New("IPv6 prefix format is incorrect")
)
func validateIPv6Prefix(prefix string) (err error) {
prefix = strings.TrimPrefix(prefix, "/")
const base, bits = 10, 8
_, err = strconv.ParseUint(prefix, base, bits)
if err != nil {
return fmt.Errorf("%w: cannot parse %q as uint8", ErrIPv6PrefixFormat, prefix)
}
const maxBits = 128
if bits > maxBits {
return fmt.Errorf("%w: %d bits cannot be larger than %d",
ErrIPv6PrefixFormat, bits, maxBits)
}
return nil
}
func (i IPv6) String() string {
return i.toLinesNode().String()
}
func (i IPv6) toLinesNode() *gotree.Node {
node := gotree.New("IPv6")
node.Appendf("Prefix: %s", i.Prefix)
return node
}
func (i *IPv6) read(reader *reader.Reader) {
i.Prefix = reader.String("IPV6_PREFIX")
}

View File

@@ -1,56 +0,0 @@
package config
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_validateIPv6Prefix(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
prefixDecimal string
err error
}{
"empty": {
err: fmt.Errorf(`IPv6 prefix format is incorrect: ` +
`cannot parse "" as uint8`),
},
"malformed": {
prefixDecimal: "malformed",
err: fmt.Errorf(`IPv6 prefix format is incorrect: ` +
`cannot parse "malformed" as uint8`),
},
"with leading slash": {
prefixDecimal: "/78",
},
"without leading slash": {
prefixDecimal: "78",
},
"full IPv6 mask": {
prefixDecimal: "/128",
},
"zero IPv6 mask": {
prefixDecimal: "/0",
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
err := validateIPv6Prefix(testCase.prefixDecimal)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
})
}
}

View File

@@ -12,7 +12,6 @@ type Config struct {
Update Update Update Update
PubIP PubIP PubIP PubIP
Resolver Resolver Resolver Resolver
IPv6 IPv6
Server Server Server Server
Health Health Health Health
Paths Paths Paths Paths
@@ -26,7 +25,6 @@ func (c *Config) SetDefaults() {
c.Update.setDefaults() c.Update.setDefaults()
c.PubIP.setDefaults() c.PubIP.setDefaults()
c.Resolver.setDefaults() c.Resolver.setDefaults()
c.IPv6.setDefaults()
c.Server.setDefaults() c.Server.setDefaults()
c.Health.SetDefaults() c.Health.SetDefaults()
c.Paths.setDefaults() c.Paths.setDefaults()
@@ -44,7 +42,6 @@ func (c Config) Validate() (err error) {
"update": &c.Update, "update": &c.Update,
"public ip": &c.PubIP, "public ip": &c.PubIP,
"resolver": &c.Resolver, "resolver": &c.Resolver,
"ipv6": &c.IPv6,
"server": &c.Server, "server": &c.Server,
"health": &c.Health, "health": &c.Health,
"paths": &c.Paths, "paths": &c.Paths,
@@ -73,7 +70,6 @@ func (c Config) toLinesNode() *gotree.Node {
node.AppendNode(c.Update.toLinesNode()) node.AppendNode(c.Update.toLinesNode())
node.AppendNode(c.PubIP.toLinesNode()) node.AppendNode(c.PubIP.toLinesNode())
node.AppendNode(c.Resolver.ToLinesNode()) node.AppendNode(c.Resolver.ToLinesNode())
node.AppendNode(c.IPv6.toLinesNode())
node.AppendNode(c.Server.toLinesNode()) node.AppendNode(c.Server.toLinesNode())
node.AppendNode(c.Health.toLinesNode()) node.AppendNode(c.Health.toLinesNode())
node.AppendNode(c.Paths.toLinesNode()) node.AppendNode(c.Paths.toLinesNode())
@@ -105,8 +101,6 @@ func (c *Config) Read(reader *reader.Reader,
return fmt.Errorf("reading resolver settings: %w", err) return fmt.Errorf("reading resolver settings: %w", err)
} }
c.IPv6.read(reader)
err = c.Server.read(reader, warner) err = c.Server.read(reader, warner)
if err != nil { if err != nil {
return fmt.Errorf("reading server settings: %w", err) return fmt.Errorf("reading server settings: %w", err)

View File

@@ -33,8 +33,6 @@ func Test_Settings_String(t *testing.T) {
| └── DNS over TLS providers | └── DNS over TLS providers
| └── all | └── all
├── Resolver: use Go default resolver ├── Resolver: use Go default resolver
├── IPv6
| └── Prefix: /128
├── Server ├── Server
| ├── Listening address: :8000 | ├── Listening address: :8000
| └── Root URL: / | └── Root URL: /

View File

@@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/fs" "io/fs"
"net/netip"
"os" "os"
"strings" "strings"
@@ -16,10 +17,11 @@ import (
) )
type commonSettings struct { type commonSettings struct {
Provider string `json:"provider"` Provider string `json:"provider"`
Domain string `json:"domain"` Domain string `json:"domain"`
Host string `json:"host"` Host string `json:"host"`
IPVersion string `json:"ip_version"` IPVersion string `json:"ip_version"`
IPv6Suffix netip.Prefix `json:"ipv6_suffix,omitempty"`
// Retro values for warnings // Retro values for warnings
IPMethod *string `json:"ip_method,omitempty"` IPMethod *string `json:"ip_method,omitempty"`
Delay *uint64 `json:"delay,omitempty"` Delay *uint64 `json:"delay,omitempty"`
@@ -165,10 +167,16 @@ func makeSettingsFromObject(common commonSettings, rawSettings json.RawMessage)
return nil, nil, err return nil, nil, err
} }
if ipVersion != ipversion.IP6 && common.IPv6Suffix.IsValid() {
warnings = append(warnings,
fmt.Sprintf("IPv6 suffix specified as %s but IP version is %s",
common.IPv6Suffix, ipVersion))
}
providers = make([]provider.Provider, len(hosts)) providers = make([]provider.Provider, len(hosts))
for i, host := range hosts { for i, host := range hosts {
providers[i], err = provider.New(providerName, rawSettings, common.Domain, providers[i], err = provider.New(providerName, rawSettings, common.Domain,
host, ipVersion) host, ipVersion, common.IPv6Suffix)
if err != nil { if err != nil {
return nil, warnings, err return nil, warnings, err
} }

View File

@@ -64,6 +64,7 @@ type Provider interface {
HTML() models.HTMLRow HTML() models.HTMLRow
Proxied() bool Proxied() bool
IPVersion() ipversion.IPVersion IPVersion() ipversion.IPVersion
IPv6Suffix() netip.Prefix
Update(ctx context.Context, client *http.Client, ip netip.Addr) (newIP netip.Addr, err error) Update(ctx context.Context, client *http.Client, ip netip.Addr) (newIP netip.Addr, err error)
} }
@@ -71,94 +72,94 @@ var ErrProviderUnknown = errors.New("unknown provider")
//nolint:gocyclo //nolint:gocyclo
func New(providerName models.Provider, data json.RawMessage, domain, host string, //nolint:ireturn func New(providerName models.Provider, data json.RawMessage, domain, host string, //nolint:ireturn
ipVersion ipversion.IPVersion) (provider Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (provider Provider, err error) {
switch providerName { switch providerName {
case constants.Aliyun: case constants.Aliyun:
return aliyun.New(data, domain, host, ipVersion) return aliyun.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.AllInkl: case constants.AllInkl:
return allinkl.New(data, domain, host, ipVersion) return allinkl.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Cloudflare: case constants.Cloudflare:
return cloudflare.New(data, domain, host, ipVersion) return cloudflare.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Custom: case constants.Custom:
return custom.New(data, domain, host, ipVersion) return custom.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Dd24: case constants.Dd24:
return dd24.New(data, domain, host, ipVersion) return dd24.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.DdnssDe: case constants.DdnssDe:
return ddnss.New(data, domain, host, ipVersion) return ddnss.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.DeSEC: case constants.DeSEC:
return desec.New(data, domain, host, ipVersion) return desec.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.DigitalOcean: case constants.DigitalOcean:
return digitalocean.New(data, domain, host, ipVersion) return digitalocean.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.DNSOMatic: case constants.DNSOMatic:
return dnsomatic.New(data, domain, host, ipVersion) return dnsomatic.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.DNSPod: case constants.DNSPod:
return dnspod.New(data, domain, host, ipVersion) return dnspod.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.DonDominio: case constants.DonDominio:
return dondominio.New(data, domain, host, ipVersion) return dondominio.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Dreamhost: case constants.Dreamhost:
return dreamhost.New(data, domain, host, ipVersion) return dreamhost.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.DuckDNS: case constants.DuckDNS:
return duckdns.New(data, domain, host, ipVersion) return duckdns.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Dyn: case constants.Dyn:
return dyn.New(data, domain, host, ipVersion) return dyn.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Dynu: case constants.Dynu:
return dynu.New(data, domain, host, ipVersion) return dynu.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.DynV6: case constants.DynV6:
return dynv6.New(data, domain, host, ipVersion) return dynv6.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.EasyDNS: case constants.EasyDNS:
return easydns.New(data, domain, host, ipVersion) return easydns.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.FreeDNS: case constants.FreeDNS:
return freedns.New(data, domain, host, ipVersion) return freedns.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Gandi: case constants.Gandi:
return gandi.New(data, domain, host, ipVersion) return gandi.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.GCP: case constants.GCP:
return gcp.New(data, domain, host, ipVersion) return gcp.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.GoDaddy: case constants.GoDaddy:
return godaddy.New(data, domain, host, ipVersion) return godaddy.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.HE: case constants.HE:
return he.New(data, domain, host, ipVersion) return he.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Hetzner: case constants.Hetzner:
return hetzner.New(data, domain, host, ipVersion) return hetzner.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Infomaniak: case constants.Infomaniak:
return infomaniak.New(data, domain, host, ipVersion) return infomaniak.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.INWX: case constants.INWX:
return inwx.New(data, domain, host, ipVersion) return inwx.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Ionos: case constants.Ionos:
return ionos.New(data, domain, host, ipVersion) return ionos.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Linode: case constants.Linode:
return linode.New(data, domain, host, ipVersion) return linode.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.LuaDNS: case constants.LuaDNS:
return luadns.New(data, domain, host, ipVersion) return luadns.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Namecheap: case constants.Namecheap:
return namecheap.New(data, domain, host) return namecheap.New(data, domain, host)
case constants.NameCom: case constants.NameCom:
return namecom.New(data, domain, host, ipVersion) return namecom.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Netcup: case constants.Netcup:
return netcup.New(data, domain, host, ipVersion) return netcup.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Njalla: case constants.Njalla:
return njalla.New(data, domain, host, ipVersion) return njalla.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.NoIP: case constants.NoIP:
return noip.New(data, domain, host, ipVersion) return noip.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.NowDNS: case constants.NowDNS:
return nowdns.New(data, domain, ipVersion) return nowdns.New(data, domain, ipVersion, ipv6Suffix)
case constants.OpenDNS: case constants.OpenDNS:
return opendns.New(data, domain, host, ipVersion) return opendns.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.OVH: case constants.OVH:
return ovh.New(data, domain, host, ipVersion) return ovh.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Porkbun: case constants.Porkbun:
return porkbun.New(data, domain, host, ipVersion) return porkbun.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.SelfhostDe: case constants.SelfhostDe:
return selfhostde.New(data, domain, host, ipVersion) return selfhostde.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Servercow: case constants.Servercow:
return servercow.New(data, domain, host, ipVersion) return servercow.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Spdyn: case constants.Spdyn:
return spdyn.New(data, domain, host, ipVersion) return spdyn.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Strato: case constants.Strato:
return strato.New(data, domain, host, ipVersion) return strato.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Variomedia: case constants.Variomedia:
return variomedia.New(data, domain, host, ipVersion) return variomedia.New(data, domain, host, ipVersion, ipv6Suffix)
case constants.Zoneedit: case constants.Zoneedit:
return zoneedit.New(data, domain, host, ipVersion) return zoneedit.New(data, domain, host, ipVersion, ipv6Suffix)
default: default:
return nil, fmt.Errorf("%w: %s", ErrProviderUnknown, providerName) return nil, fmt.Errorf("%w: %s", ErrProviderUnknown, providerName)
} }

View File

@@ -19,13 +19,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
accessKeyID string accessKeyID string
accessSecret string accessSecret string
region string region string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
AccessKeyID string `json:"access_key_id"` AccessKeyID string `json:"access_key_id"`
AccessSecret string `json:"access_secret"` AccessSecret string `json:"access_secret"`
@@ -39,6 +41,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
accessKeyID: extraSettings.AccessKeyID, accessKeyID: extraSettings.AccessKeyID,
accessSecret: extraSettings.AccessSecret, accessSecret: extraSettings.AccessSecret,
region: "cn-hangzhou", region: "cn-hangzhou",
@@ -79,6 +82,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -23,13 +23,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -43,6 +45,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -82,6 +85,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -108,8 +115,9 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("host", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("host", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if ip.Is6() { // ipv6 if !useProviderIP {
if ip.Is6() {
values.Set("myip6", ip.String()) values.Set("myip6", ip.String())
} else { } else {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
@@ -171,7 +179,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
newIP = ips[0] newIP = ips[0]
if !p.useProviderIP && ip.Compare(newIP) != 0 { if !useProviderIP && ip.Compare(newIP) != 0 {
return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s", return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s",
errors.ErrIPReceivedMismatch, ip, newIP) errors.ErrIPReceivedMismatch, ip, newIP)
} }

View File

@@ -24,6 +24,7 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
key string key string
token string token string
email string email string
@@ -34,7 +35,8 @@ type Provider struct {
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Key string `json:"key"` Key string `json:"key"`
Token string `json:"token"` Token string `json:"token"`
@@ -52,6 +54,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
key: extraSettings.Key, key: extraSettings.Key,
token: extraSettings.Token, token: extraSettings.Token,
email: extraSettings.Email, email: extraSettings.Email,
@@ -116,6 +119,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return p.proxied return p.proxied
} }

View File

@@ -22,6 +22,7 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
url *url.URL url *url.URL
ipv4Key string ipv4Key string
ipv6Key string ipv6Key string
@@ -29,7 +30,8 @@ type Provider struct {
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
URL string `json:"url"` URL string `json:"url"`
IPv4Key string `json:"ipv4key"` IPv4Key string `json:"ipv4key"`
@@ -50,6 +52,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
url: parsedURL, url: parsedURL,
ipv4Key: extraSettings.IPv4Key, ipv4Key: extraSettings.IPv4Key,
ipv6Key: extraSettings.IPv6Key, ipv6Key: extraSettings.IPv6Key,
@@ -95,6 +98,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -22,12 +22,13 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) ( ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) { p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Password string `json:"password"` Password string `json:"password"`
@@ -41,6 +42,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
} }
@@ -74,6 +76,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -101,7 +107,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
values := url.Values{} values := url.Values{}
values.Set("hostname", p.BuildDomainName()) values.Set("hostname", p.BuildDomainName())
values.Set("password", p.password) values.Set("password", p.password)
if p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if useProviderIP {
values.Set("ip", "auto") values.Set("ip", "auto")
} else { } else {
values.Set("ip", ip.String()) values.Set("ip", ip.String())

View File

@@ -22,6 +22,7 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
dualStack bool dualStack bool
@@ -29,7 +30,8 @@ type Provider struct {
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -44,6 +46,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
dualStack: extraSettings.DualStack, dualStack: extraSettings.DualStack,
@@ -84,6 +87,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -111,7 +118,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
values.Set("user", p.username) values.Set("user", p.username)
values.Set("pwd", p.password) values.Set("pwd", p.password)
values.Set("host", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("host", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
ipKey := "ip" ipKey := "ip"
if p.dualStack && ip.Is6() { // ipv6 update for dual stack if p.dualStack && ip.Is6() { // ipv6 update for dual stack
ipKey = "ip6" ipKey = "ip6"

View File

@@ -22,12 +22,14 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
token string token string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Token string `json:"token"` Token string `json:"token"`
UseProviderIP bool `json:"provider_ip"` UseProviderIP bool `json:"provider_ip"`
@@ -43,6 +45,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
token: extraSettings.Token, token: extraSettings.Token,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
} }
@@ -76,6 +79,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -102,7 +109,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
u.RawQuery = values.Encode() u.RawQuery = values.Encode()

View File

@@ -18,14 +18,16 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
token string ipv6Suffix netip.Prefix
token string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Token string `json:"token"` Token string `json:"token"`
}{} }{}
@@ -34,10 +36,11 @@ func New(data json.RawMessage, domain, host string,
return nil, err return nil, err
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
token: extraSettings.Token, ipv6Suffix: ipv6Suffix,
token: extraSettings.Token,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -69,6 +72,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -23,13 +23,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -43,6 +45,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -80,6 +83,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return p.host == "all" return p.host == "all"
} }
@@ -106,7 +113,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
User: url.UserPassword(p.username, p.password), User: url.UserPassword(p.username, p.password),
} }
values := url.Values{} values := url.Values{}
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
values.Set("wildcard", "NOCHG") values.Set("wildcard", "NOCHG")
@@ -172,7 +180,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
newIP = ips[0] newIP = ips[0]
if !p.useProviderIP && ip.Compare(newIP) != 0 { if !useProviderIP && ip.Compare(newIP) != 0 {
return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s", return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s",
errors.ErrIPReceivedMismatch, ip, newIP) errors.ErrIPReceivedMismatch, ip, newIP)
} }

View File

@@ -19,14 +19,16 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
token string ipv6Suffix netip.Prefix
token string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Token string `json:"token"` Token string `json:"token"`
}{} }{}
@@ -35,10 +37,11 @@ func New(data json.RawMessage, domain, host string,
return nil, err return nil, err
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
token: extraSettings.Token, ipv6Suffix: ipv6Suffix,
token: extraSettings.Token,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -70,6 +73,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -18,16 +18,18 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
username string ipv6Suffix netip.Prefix
key string username string
name string key string
name string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` // retro-compatibility Password string `json:"password"` // retro-compatibility
@@ -46,12 +48,13 @@ func New(data json.RawMessage, domain, host string,
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
username: extraSettings.Username, ipv6Suffix: ipv6Suffix,
key: extraSettings.Key, username: extraSettings.Username,
name: extraSettings.Name, key: extraSettings.Key,
name: extraSettings.Name,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -88,6 +91,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -19,14 +19,16 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
key string ipv6Suffix netip.Prefix
key string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Key string `json:"key"` Key string `json:"key"`
}{} }{}
@@ -38,10 +40,11 @@ func New(data json.RawMessage, domain, host string,
host = "@" // default host = "@" // default
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
key: extraSettings.Key, ipv6Suffix: ipv6Suffix,
key: extraSettings.Key,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -76,6 +79,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -22,12 +22,14 @@ import (
type Provider struct { type Provider struct {
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
token string token string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, _, host string, func New(data json.RawMessage, _, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Token string `json:"token"` Token string `json:"token"`
UseProviderIP bool `json:"provider_ip"` UseProviderIP bool `json:"provider_ip"`
@@ -39,6 +41,7 @@ func New(data json.RawMessage, _, host string,
p = &Provider{ p = &Provider{
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
token: extraSettings.Token, token: extraSettings.Token,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
} }
@@ -52,12 +55,11 @@ func New(data json.RawMessage, _, host string,
var tokenRegex = regexp.MustCompile(`^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$`) var tokenRegex = regexp.MustCompile(`^[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12}$`)
func (p *Provider) isValid() error { func (p *Provider) isValid() error {
if !tokenRegex.MatchString(p.token) { switch {
case !tokenRegex.MatchString(p.token):
return fmt.Errorf("%w: token %q does not match regex %q", return fmt.Errorf("%w: token %q does not match regex %q",
errors.ErrTokenNotValid, p.token, tokenRegex) errors.ErrTokenNotValid, p.token, tokenRegex)
} case p.host == "@", p.host == "*":
switch p.host {
case "@", "*":
return fmt.Errorf("%w: %q is not valid", return fmt.Errorf("%w: %q is not valid",
errors.ErrHostOnlySubdomain, p.host) errors.ErrHostOnlySubdomain, p.host)
} }
@@ -80,6 +82,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -107,7 +113,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
values.Set("verbose", "true") values.Set("verbose", "true")
values.Set("domains", p.host) values.Set("domains", p.host)
values.Set("token", p.token) values.Set("token", p.token)
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
if ip.Is6() { if ip.Is6() {
values.Set("ipv6", ip.String()) values.Set("ipv6", ip.String())
} else { } else {
@@ -156,7 +163,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
return netip.Addr{}, fmt.Errorf("%w", errors.ErrReceivedNoIP) return netip.Addr{}, fmt.Errorf("%w", errors.ErrReceivedNoIP)
} }
newIP = ips[0] newIP = ips[0]
if !p.useProviderIP && newIP.Compare(ip) != 0 { if !useProviderIP && newIP.Compare(ip) != 0 {
return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s", return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s",
errors.ErrIPReceivedMismatch, ip, newIP) errors.ErrIPReceivedMismatch, ip, newIP)
} }

View File

@@ -19,15 +19,17 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
username string ipv6Suffix netip.Prefix
clientKey string username string
clientKey string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` // Retro-compatibility Password string `json:"password"` // Retro-compatibility
@@ -44,11 +46,12 @@ func New(data json.RawMessage, domain, host string,
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
username: extraSettings.Username, ipv6Suffix: ipv6Suffix,
clientKey: clientKey, username: extraSettings.Username,
clientKey: clientKey,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -83,6 +86,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -21,15 +21,17 @@ import (
type Provider struct { type Provider struct {
domain string domain string
host string host string
group string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
group string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -49,6 +51,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
group: extraSettings.Group, group: extraSettings.Group,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
@@ -89,6 +92,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -118,7 +125,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
values.Set("location", p.group) values.Set("location", p.group)
hostname := utils.BuildDomainName(p.host, p.domain) hostname := utils.BuildDomainName(p.host, p.domain)
values.Set("hostname", hostname) values.Set("hostname", hostname)
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
if ip.Is6() { if ip.Is6() {
values.Set("myipv6", ip.String()) values.Set("myipv6", ip.String())
} else { } else {

View File

@@ -20,12 +20,14 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
token string token string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Token string `json:"token"` Token string `json:"token"`
UseProviderIP bool `json:"provider_ip"` UseProviderIP bool `json:"provider_ip"`
@@ -38,6 +40,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
token: extraSettings.Token, token: extraSettings.Token,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
} }
@@ -74,6 +77,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -108,7 +115,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
values.Set("token", p.token) values.Set("token", p.token)
values.Set("zone", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("zone", utils.BuildURLQueryHostname(p.host, p.domain))
ipValue := ip.String() ipValue := ip.String()
if p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if useProviderIP {
ipValue = "auto" ipValue = "auto"
} }
if isIPv4 { if isIPv4 {

View File

@@ -22,13 +22,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
token string token string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Token string `json:"token"` Token string `json:"token"`
@@ -42,6 +44,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
token: extraSettings.Token, token: extraSettings.Token,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -79,6 +82,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -106,7 +113,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
if p.host == "*" { if p.host == "*" {

View File

@@ -19,14 +19,16 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
token string ipv6Suffix netip.Prefix
token string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Token string `json:"token"` Token string `json:"token"`
}{} }{}
@@ -35,10 +37,11 @@ func New(data json.RawMessage, domain, host string,
return nil, err return nil, err
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
token: extraSettings.Token, ipv6Suffix: ipv6Suffix,
token: extraSettings.Token,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -74,6 +77,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) BuildDomainName() string { func (p *Provider) BuildDomainName() string {
return utils.BuildDomainName(p.host, p.domain) return utils.BuildDomainName(p.host, p.domain)
} }

View File

@@ -18,10 +18,11 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ttl int ipVersion ipversion.IPVersion
ipVersion ipversion.IPVersion ipv6Suffix netip.Prefix
ttl int
// Authentication, either use the personal access token // Authentication, either use the personal access token
// or the deprecated API key. // or the deprecated API key.
// See https://api.gandi.net/docs/authentication/ // See https://api.gandi.net/docs/authentication/
@@ -32,7 +33,8 @@ type Provider struct {
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
PersonalAccessToken string `json:"personal_access_token"` PersonalAccessToken string `json:"personal_access_token"`
APIKey string `json:"key"` APIKey string `json:"key"`
@@ -46,6 +48,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
personalAccessToken: extraSettings.PersonalAccessToken, personalAccessToken: extraSettings.PersonalAccessToken,
apiKey: extraSettings.APIKey, apiKey: extraSettings.APIKey,
ttl: extraSettings.TTL, ttl: extraSettings.TTL,
@@ -80,6 +83,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -3,6 +3,7 @@ package gcp
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/netip"
"github.com/qdm12/ddns-updater/internal/models" "github.com/qdm12/ddns-updater/internal/models"
"github.com/qdm12/ddns-updater/internal/provider/constants" "github.com/qdm12/ddns-updater/internal/provider/constants"
@@ -14,14 +15,16 @@ import (
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
project string project string
zone string zone string
credentials json.RawMessage credentials json.RawMessage
ipVersion ipversion.IPVersion
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
var extraSettings struct { var extraSettings struct {
Project string `json:"project"` Project string `json:"project"`
Zone string `json:"zone"` Zone string `json:"zone"`
@@ -37,6 +40,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
project: extraSettings.Project, project: extraSettings.Project,
zone: extraSettings.Zone, zone: extraSettings.Zone,
credentials: extraSettings.Credentials, credentials: extraSettings.Credentials,
@@ -82,6 +86,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -20,15 +20,17 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
key string ipv6Suffix netip.Prefix
secret string key string
secret string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Key string `json:"key"` Key string `json:"key"`
Secret string `json:"secret"` Secret string `json:"secret"`
@@ -38,11 +40,12 @@ func New(data json.RawMessage, domain, host string,
return nil, err return nil, err
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
key: extraSettings.Key, ipv6Suffix: ipv6Suffix,
secret: extraSettings.Secret, key: extraSettings.Key,
secret: extraSettings.Secret,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -80,6 +83,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -23,12 +23,14 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Password string `json:"password"` Password string `json:"password"`
UseProviderIP bool `json:"provider_ip"` UseProviderIP bool `json:"provider_ip"`
@@ -41,6 +43,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
} }
@@ -74,6 +77,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -101,7 +108,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", fqdn) values.Set("hostname", fqdn)
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
u.RawQuery = values.Encode() u.RawQuery = values.Encode()
@@ -147,7 +155,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
newIP = ips[0] newIP = ips[0]
if !p.useProviderIP && ip.Compare(newIP) != 0 { if !useProviderIP && ip.Compare(newIP) != 0 {
return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s", return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s",
errors.ErrIPReceivedMismatch, ip, newIP) errors.ErrIPReceivedMismatch, ip, newIP)
} }

View File

@@ -19,13 +19,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
token string token string
zoneIdentifier string zoneIdentifier string
ttl uint ttl uint
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Token string `json:"token"` Token string `json:"token"`
ZoneIdentifier string `json:"zone_identifier"` ZoneIdentifier string `json:"zone_identifier"`
@@ -39,6 +41,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
token: extraSettings.Token, token: extraSettings.Token,
zoneIdentifier: extraSettings.ZoneIdentifier, zoneIdentifier: extraSettings.ZoneIdentifier,
ttl: extraSettings.TTL, ttl: extraSettings.TTL,
@@ -79,6 +82,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -22,13 +22,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -42,6 +44,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -81,6 +84,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -107,7 +114,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
u.RawQuery = values.Encode() u.RawQuery = values.Encode()
@@ -152,7 +160,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
if err != nil { if err != nil {
return netip.Addr{}, fmt.Errorf("%w: for response %q: %w", return netip.Addr{}, fmt.Errorf("%w: for response %q: %w",
errors.ErrIPReceivedMalformed, ipString, err) errors.ErrIPReceivedMalformed, ipString, err)
} else if !p.useProviderIP && ip.Compare(newIP) != 0 { } else if !useProviderIP && ip.Compare(newIP) != 0 {
return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s", return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s",
errors.ErrIPReceivedMismatch, ip, newIP) errors.ErrIPReceivedMismatch, ip, newIP)
} }

View File

@@ -19,15 +19,17 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
username string ipv6Suffix netip.Prefix
password string username string
password string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -37,11 +39,12 @@ func New(data json.RawMessage, domain, host string,
return nil, fmt.Errorf("decoding inwx extra settings: %w", err) return nil, fmt.Errorf("decoding inwx extra settings: %w", err)
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
username: extraSettings.Username, ipv6Suffix: ipv6Suffix,
password: extraSettings.Password, username: extraSettings.Username,
password: extraSettings.Password,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -78,6 +81,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -15,14 +15,16 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
apiKey string ipv6Suffix netip.Prefix
apiKey string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
APIKey string `json:"api_key"` APIKey string `json:"api_key"`
}{} }{}
@@ -30,10 +32,11 @@ func New(data json.RawMessage, domain, host string,
return nil, fmt.Errorf("decoding ionos extra settings: %w", err) return nil, fmt.Errorf("decoding ionos extra settings: %w", err)
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
apiKey: extraSettings.APIKey, ipv6Suffix: ipv6Suffix,
apiKey: extraSettings.APIKey,
} }
if err := p.isValid(); err != nil { if err := p.isValid(); err != nil {
return nil, err return nil, err
@@ -64,6 +67,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -20,14 +20,16 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
token string ipv6Suffix netip.Prefix
token string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Token string `json:"token"` Token string `json:"token"`
}{} }{}
@@ -36,10 +38,11 @@ func New(data json.RawMessage, domain, host string,
return nil, err return nil, err
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
token: extraSettings.Token, ipv6Suffix: ipv6Suffix,
token: extraSettings.Token,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -71,6 +74,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -20,15 +20,17 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
email string ipv6Suffix netip.Prefix
token string email string
token string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Email string `json:"email"` Email string `json:"email"`
Token string `json:"token"` Token string `json:"token"`
@@ -38,11 +40,12 @@ func New(data json.RawMessage, domain, host string,
return nil, err return nil, err
} }
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
email: extraSettings.Email, ipv6Suffix: ipv6Suffix,
token: extraSettings.Token, email: extraSettings.Email,
token: extraSettings.Token,
} }
err = p.isValid() err = p.isValid()
if err != nil { if err != nil {
@@ -82,6 +85,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -16,16 +16,18 @@ import (
) )
type Provider struct { type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
username string ipv6Suffix netip.Prefix
token string username string
ttl *uint32 token string
ttl *uint32
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Token string `json:"token"` Token string `json:"token"`
@@ -48,12 +50,13 @@ func New(data json.RawMessage, domain, host string,
} }
return &Provider{ return &Provider{
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
username: extraSettings.Username, ipv6Suffix: ipv6Suffix,
token: extraSettings.Token, username: extraSettings.Username,
ttl: extraSettings.TTL, token: extraSettings.Token,
ttl: extraSettings.TTL,
}, nil }, nil
} }
@@ -73,6 +76,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -15,16 +15,18 @@ import (
) )
type Provider struct { type Provider struct {
customerNumber string
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
customerNumber string
apiKey string apiKey string
password string password string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
var extraSettings struct { var extraSettings struct {
CustomerNumber string `json:"customer_number"` CustomerNumber string `json:"customer_number"`
APIKey string `json:"api_key"` APIKey string `json:"api_key"`
@@ -39,6 +41,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
customerNumber: extraSettings.CustomerNumber, customerNumber: extraSettings.CustomerNumber,
apiKey: extraSettings.APIKey, apiKey: extraSettings.APIKey,
password: extraSettings.Password, password: extraSettings.Password,
@@ -82,6 +85,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -20,12 +20,14 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
key string key string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Key string `json:"key"` Key string `json:"key"`
UseProviderIP bool `json:"provider_ip"` UseProviderIP bool `json:"provider_ip"`
@@ -38,6 +40,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
key: extraSettings.Key, key: extraSettings.Key,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
} }
@@ -71,6 +74,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -98,7 +105,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
values.Set("h", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("h", utils.BuildURLQueryHostname(p.host, p.domain))
values.Set("k", p.key) values.Set("k", p.key)
updatingIP6 := ip.Is6() updatingIP6 := ip.Is6()
if p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if useProviderIP {
values.Set("auto", "") values.Set("auto", "")
} else { } else {
if updatingIP6 { if updatingIP6 {
@@ -147,7 +155,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
newIP, err = netip.ParseAddr(ipString) newIP, err = netip.ParseAddr(ipString)
if err != nil { if err != nil {
return netip.Addr{}, fmt.Errorf("%w: %w", errors.ErrIPReceivedMalformed, err) return netip.Addr{}, fmt.Errorf("%w: %w", errors.ErrIPReceivedMalformed, err)
} else if !p.useProviderIP && ip.Compare(newIP) != 0 { } else if !useProviderIP && ip.Compare(newIP) != 0 {
return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s", return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s",
errors.ErrIPReceivedMismatch, ip, newIP) errors.ErrIPReceivedMismatch, ip, newIP)
} }

View File

@@ -23,13 +23,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -43,6 +45,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -85,6 +88,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -111,7 +118,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
if ip.Is6() { if ip.Is6() {
values.Set("myipv6", ip.String()) values.Set("myipv6", ip.String())
} else { } else {
@@ -170,12 +178,12 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
ips = ipextract.IPv6(s) ips = ipextract.IPv6(s)
} }
if !p.useProviderIP && len(ips) == 0 { if !useProviderIP && len(ips) == 0 {
return netip.Addr{}, fmt.Errorf("%w", errors.ErrReceivedNoIP) return netip.Addr{}, fmt.Errorf("%w", errors.ErrReceivedNoIP)
} }
newIP = ips[0] newIP = ips[0]
if !p.useProviderIP && ip.Compare(newIP) != 0 { if !useProviderIP && ip.Compare(newIP) != 0 {
return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s", return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s",
errors.ErrIPReceivedMismatch, ip, newIP) errors.ErrIPReceivedMismatch, ip, newIP)
} }

View File

@@ -21,13 +21,15 @@ import (
type Provider struct { type Provider struct {
domain string domain string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain string, func New(data json.RawMessage, domain string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -40,6 +42,7 @@ func New(data json.RawMessage, domain string,
p = &Provider{ p = &Provider{
domain: domain, domain: domain,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -77,6 +80,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -104,7 +111,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
values := url.Values{} values := url.Values{}
values.Set("hostname", p.domain) values.Set("hostname", p.domain)
if !p.useProviderIP { if !p.useProviderIP || (ip.Is6() && p.ipv6Suffix.IsValid()) {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
u.RawQuery = values.Encode() u.RawQuery = values.Encode()

View File

@@ -22,13 +22,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -42,6 +44,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -81,6 +84,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -107,7 +114,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
u.RawQuery = values.Encode() u.RawQuery = values.Encode()
@@ -141,7 +149,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
newIP, err = netip.ParseAddr(responseIPString) newIP, err = netip.ParseAddr(responseIPString)
if err != nil { if err != nil {
return netip.Addr{}, fmt.Errorf("%w: %w", errors.ErrIPReceivedMalformed, err) return netip.Addr{}, fmt.Errorf("%w: %w", errors.ErrIPReceivedMalformed, err)
} else if !p.useProviderIP && newIP.Compare(ip) != 0 { } else if !useProviderIP && newIP.Compare(ip) != 0 {
return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s", return netip.Addr{}, fmt.Errorf("%w: sent ip %s to update but received %s",
errors.ErrIPReceivedMismatch, ip, newIP) errors.ErrIPReceivedMismatch, ip, newIP)
} }

View File

@@ -23,6 +23,7 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
@@ -36,7 +37,8 @@ type Provider struct {
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -61,6 +63,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -117,6 +120,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -145,7 +152,8 @@ func (p *Provider) updateWithDynHost(ctx context.Context, client *http.Client,
values := url.Values{} values := url.Values{}
values.Set("system", "dyndns") values.Set("system", "dyndns")
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
u.RawQuery = values.Encode() u.RawQuery = values.Encode()

View File

@@ -18,14 +18,16 @@ import (
type Provider struct { type Provider struct {
domain string domain string
host string host string
ttl uint
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
ttl uint
apiKey string apiKey string
secretAPIKey string secretAPIKey string
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
SecretAPIKey string `json:"secret_api_key"` SecretAPIKey string `json:"secret_api_key"`
APIKey string `json:"api_key"` APIKey string `json:"api_key"`
@@ -39,6 +41,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
secretAPIKey: extraSettings.SecretAPIKey, secretAPIKey: extraSettings.SecretAPIKey,
apiKey: extraSettings.APIKey, apiKey: extraSettings.APIKey,
ttl: extraSettings.TTL, ttl: extraSettings.TTL,
@@ -76,6 +79,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -22,13 +22,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
@@ -42,6 +44,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -81,6 +84,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -107,7 +114,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
u.RawQuery = values.Encode() u.RawQuery = values.Encode()

View File

@@ -22,13 +22,14 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
password string password string
useProviderIP bool useProviderIP bool
ttl uint ttl uint
} }
func New(data json.RawMessage, domain, host string, ipVersion ipversion.IPVersion) ( func New(data json.RawMessage, domain, host string, ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) { p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
@@ -45,6 +46,7 @@ func New(data json.RawMessage, domain, host string, ipVersion ipversion.IPVersio
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -85,6 +87,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -22,6 +22,7 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
user string user string
password string password string
token string token string
@@ -29,7 +30,8 @@ type Provider struct {
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
User string `json:"user"` User string `json:"user"`
Password string `json:"password"` Password string `json:"password"`
@@ -44,6 +46,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
user: extraSettings.User, user: extraSettings.User,
password: extraSettings.Password, password: extraSettings.Password,
token: extraSettings.Token, token: extraSettings.Token,
@@ -88,6 +91,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }

View File

@@ -22,12 +22,14 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Password string `json:"password"` Password string `json:"password"`
UseProviderIP bool `json:"provider_ip"` UseProviderIP bool `json:"provider_ip"`
@@ -40,6 +42,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
} }
@@ -76,6 +79,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -102,7 +109,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
u.RawQuery = values.Encode() u.RawQuery = values.Encode()

View File

@@ -22,13 +22,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
email string email string
password string password string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"` Password string `json:"password"`
@@ -42,6 +44,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
email: extraSettings.Email, email: extraSettings.Email,
password: extraSettings.Password, password: extraSettings.Password,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -81,6 +84,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -100,7 +107,8 @@ func (p *Provider) HTML() models.HTMLRow {
func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Addr) (newIP netip.Addr, err error) { func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Addr) (newIP netip.Addr, err error) {
host := "dyndns.variomedia.de" host := "dyndns.variomedia.de"
if p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if useProviderIP {
if ip.Is6() { if ip.Is6() {
host = "dyndns6.variomedia.de" host = "dyndns6.variomedia.de"
} else { } else {

View File

@@ -23,13 +23,15 @@ type Provider struct {
domain string domain string
host string host string
ipVersion ipversion.IPVersion ipVersion ipversion.IPVersion
ipv6Suffix netip.Prefix
username string username string
token string token string
useProviderIP bool useProviderIP bool
} }
func New(data json.RawMessage, domain, host string, func New(data json.RawMessage, domain, host string,
ipVersion ipversion.IPVersion) (p *Provider, err error) { ipVersion ipversion.IPVersion, ipv6Suffix netip.Prefix) (
p *Provider, err error) {
extraSettings := struct { extraSettings := struct {
Username string `json:"username"` Username string `json:"username"`
Token string `json:"token"` Token string `json:"token"`
@@ -43,6 +45,7 @@ func New(data json.RawMessage, domain, host string,
domain: domain, domain: domain,
host: host, host: host,
ipVersion: ipVersion, ipVersion: ipVersion,
ipv6Suffix: ipv6Suffix,
username: extraSettings.Username, username: extraSettings.Username,
token: extraSettings.Token, token: extraSettings.Token,
useProviderIP: extraSettings.UseProviderIP, useProviderIP: extraSettings.UseProviderIP,
@@ -80,6 +83,10 @@ func (p *Provider) IPVersion() ipversion.IPVersion {
return p.ipVersion return p.ipVersion
} }
func (p *Provider) IPv6Suffix() netip.Prefix {
return p.ipv6Suffix
}
func (p *Provider) Proxied() bool { func (p *Provider) Proxied() bool {
return false return false
} }
@@ -107,7 +114,8 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
} }
values := url.Values{} values := url.Values{}
values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain)) values.Set("hostname", utils.BuildURLQueryHostname(p.host, p.domain))
if !p.useProviderIP { useProviderIP := p.useProviderIP && (ip.Is4() || !p.ipv6Suffix.IsValid())
if !useProviderIP {
values.Set("myip", ip.String()) values.Set("myip", ip.String())
} }
if p.host == "*" { if p.host == "*" {

27
internal/update/ipv6.go Normal file
View File

@@ -0,0 +1,27 @@
package update
import (
"fmt"
"net/netip"
)
func ipv6WithSuffix(publicIP netip.Addr, ipv6Suffix netip.Prefix) (
updateIP netip.Addr) {
if !publicIP.IsValid() || !publicIP.Is6() || !ipv6Suffix.IsValid() {
return publicIP
}
const ipv6Bits = 128
const bitsInByte = 8
prefixLength := (ipv6Bits - ipv6Suffix.Bits()) / bitsInByte
ispPrefix := publicIP.AsSlice()[:prefixLength]
localSuffix := ipv6Suffix.Addr().AsSlice()[prefixLength:]
ipv6Bytes := ispPrefix // ispPrefix has already 16 bytes of capacity
ipv6Bytes = append(ipv6Bytes, localSuffix...)
updateIP, ok := netip.AddrFromSlice(ipv6Bytes)
if !ok {
panic(fmt.Sprintf("failed to create IPv6 address from merged bytes %v", ipv6Bytes))
}
return updateIP
}

View File

@@ -0,0 +1,57 @@
package update
import (
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_ipv6WithSuffix(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
publicIP netip.Addr
ipv6Suffix netip.Prefix
updateIP netip.Addr
}{
"blank_inputs": {},
"ipv4_publicip": {
publicIP: netip.MustParseAddr("1.2.3.4"),
updateIP: netip.MustParseAddr("1.2.3.4"),
},
"invalid_suffix": {
publicIP: netip.MustParseAddr("2001:db8::1"),
updateIP: netip.MustParseAddr("2001:db8::1"),
},
"zero_suffix": {
publicIP: netip.MustParseAddr("e4db:af36:82e:1221:1b7f:2f54:6e9e:5e5f"),
ipv6Suffix: netip.MustParsePrefix("0:0:0:0:0:0:0:0/0"),
updateIP: netip.MustParseAddr("e4db:af36:82e:1221:1b7f:2f54:6e9e:5e5f"),
},
"suffix_64": {
publicIP: netip.MustParseAddr("e4db:af36:82e:1221:1b7f:2f54:6e9e:5e5f"),
ipv6Suffix: netip.MustParsePrefix("0:0:0:0:72ad:8fbb:a54e:bedd/64"),
updateIP: netip.MustParseAddr("e4db:af36:82e:1221:" + "72ad:8fbb:a54e:bedd"),
},
"suffix_56": {
publicIP: netip.MustParseAddr("e4db:af36:82e:1221:1b7f:2f54:6e9e:5e5f"),
ipv6Suffix: netip.MustParsePrefix("bbff:8199:4e2f:b4ba:72ad:8fbb:a54e:bedd/56"),
updateIP: netip.MustParseAddr("e4db:af36:82e:1221:1b" + "ad:8fbb:a54e:bedd"),
},
"suffix_48": {
publicIP: netip.MustParseAddr("e4db:af36:82e:1221:1b7f:2f54:6e9e:5e5f"),
ipv6Suffix: netip.MustParsePrefix("bbff:8199:4e2f:b4ba:72ad:8fbb:a54e:bedd/48"),
updateIP: netip.MustParseAddr("e4db:af36:82e:1221:1b7f:" + "8fbb:a54e:bedd"),
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
updateIP := ipv6WithSuffix(testCase.publicIP, testCase.ipv6Suffix)
assert.Equal(t, testCase.updateIP.String(), updateIP.String())
})
}
}

View File

@@ -1,15 +0,0 @@
package update
import (
"fmt"
"net/netip"
)
func mustMaskIPv6(ipv6 netip.Addr, ipv6MaskBits uint8) (maskedIPv6 netip.Addr) {
prefix, err := ipv6.Prefix(int(ipv6MaskBits))
if err != nil {
panic(fmt.Sprintf("getting masked IPv6 prefix: %s", err))
}
maskedIPv6 = prefix.Addr()
return maskedIPv6
}

View File

@@ -1,19 +0,0 @@
package update
import (
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_mustMaskIPv6(t *testing.T) {
t.Parallel()
const maskBits = 24
ip := netip.AddrFrom4([4]byte{1, 2, 3, 4})
maskedIP := mustMaskIPv6(ip, maskBits)
expected := netip.AddrFrom4([4]byte{1, 2, 3, 0})
assert.Equal(t, expected, maskedIP)
}

View File

@@ -14,37 +14,34 @@ import (
) )
type Runner struct { type Runner struct {
period time.Duration period time.Duration
db Database db Database
updater UpdaterInterface updater UpdaterInterface
force chan struct{} force chan struct{}
forceResult chan []error forceResult chan []error
ipv6MaskBits uint8 cooldown time.Duration
cooldown time.Duration resolver LookupIPer
resolver LookupIPer ipGetter PublicIPFetcher
ipGetter PublicIPFetcher logger Logger
logger Logger timeNow func() time.Time
timeNow func() time.Time hioClient HealthchecksIOClient
hioClient HealthchecksIOClient
} }
func NewRunner(db Database, updater UpdaterInterface, ipGetter PublicIPFetcher, func NewRunner(db Database, updater UpdaterInterface, ipGetter PublicIPFetcher,
period time.Duration, ipv6MaskBits uint8, cooldown time.Duration, period time.Duration, cooldown time.Duration, logger Logger, resolver LookupIPer,
logger Logger, resolver LookupIPer, timeNow func() time.Time, timeNow func() time.Time, hioClient HealthchecksIOClient) *Runner {
hioClient HealthchecksIOClient) *Runner {
return &Runner{ return &Runner{
period: period, period: period,
db: db, db: db,
updater: updater, updater: updater,
force: make(chan struct{}), force: make(chan struct{}),
forceResult: make(chan []error), forceResult: make(chan []error),
ipv6MaskBits: ipv6MaskBits, cooldown: cooldown,
cooldown: cooldown, resolver: resolver,
resolver: resolver, ipGetter: ipGetter,
ipGetter: ipGetter, logger: logger,
logger: logger, timeNow: timeNow,
timeNow: timeNow, hioClient: hioClient,
hioClient: hioClient,
} }
} }
@@ -103,15 +100,13 @@ func doIPVersion(records []librecords.Record) (doIP, doIPv4, doIPv6 bool) {
return doIP, doIPv4, doIPv6 return doIP, doIPv4, doIPv6
} }
func (r *Runner) getNewIPs(ctx context.Context, doIP, doIPv4, doIPv6 bool, ipv6MaskBits uint8) ( func (r *Runner) getNewIPs(ctx context.Context, doIP, doIPv4, doIPv6 bool) (
ip, ipv4, ipv6 netip.Addr, errors []error) { ip, ipv4, ipv6 netip.Addr, errors []error) {
var err error var err error
if doIP { if doIP {
ip, err = tryAndRepeatGettingIP(ctx, r.ipGetter.IP, r.logger, ipversion.IP4or6) ip, err = tryAndRepeatGettingIP(ctx, r.ipGetter.IP, r.logger, ipversion.IP4or6)
if err != nil { if err != nil {
errors = append(errors, err) errors = append(errors, err)
} else if ip.Is6() {
ip = mustMaskIPv6(ip, ipv6MaskBits)
} }
} }
if doIPv4 { if doIPv4 {
@@ -124,18 +119,17 @@ func (r *Runner) getNewIPs(ctx context.Context, doIP, doIPv4, doIPv6 bool, ipv6M
ipv6, err = tryAndRepeatGettingIP(ctx, r.ipGetter.IP6, r.logger, ipversion.IP6) ipv6, err = tryAndRepeatGettingIP(ctx, r.ipGetter.IP6, r.logger, ipversion.IP6)
if err != nil { if err != nil {
errors = append(errors, err) errors = append(errors, err)
} else {
ipv6 = mustMaskIPv6(ipv6, ipv6MaskBits)
} }
} }
return ip, ipv4, ipv6, errors return ip, ipv4, ipv6, errors
} }
func (r *Runner) getRecordIDsToUpdate(ctx context.Context, records []librecords.Record, func (r *Runner) getRecordIDsToUpdate(ctx context.Context, records []librecords.Record,
ip, ipv4, ipv6 netip.Addr, now time.Time, ipv6MaskBits uint8) (recordIDs map[uint]struct{}) { ip, ipv4, ipv6 netip.Addr, now time.Time) (recordIDs map[uint]struct{}) {
recordIDs = make(map[uint]struct{}) recordIDs = make(map[uint]struct{})
for i, record := range records { for i, record := range records {
if shouldUpdate := r.shouldUpdateRecord(ctx, record, ip, ipv4, ipv6, now, ipv6MaskBits); shouldUpdate { shouldUpdate := r.shouldUpdateRecord(ctx, record, ip, ipv4, ipv6, now)
if shouldUpdate {
id := uint(i) id := uint(i)
recordIDs[id] = struct{}{} recordIDs[id] = struct{}{}
} }
@@ -144,7 +138,7 @@ func (r *Runner) getRecordIDsToUpdate(ctx context.Context, records []librecords.
} }
func (r *Runner) shouldUpdateRecord(ctx context.Context, record librecords.Record, func (r *Runner) shouldUpdateRecord(ctx context.Context, record librecords.Record,
ip, ipv4, ipv6 netip.Addr, now time.Time, ipv6MaskBits uint8) (update bool) { ip, ipv4, ipv6 netip.Addr, now time.Time) (update bool) {
isWithinBanPeriod := record.LastBan != nil && now.Sub(*record.LastBan) < time.Hour isWithinBanPeriod := record.LastBan != nil && now.Sub(*record.LastBan) < time.Hour
isWithinCooldown := now.Sub(record.History.GetSuccessTime()) < r.cooldown isWithinCooldown := now.Sub(record.History.GetSuccessTime()) < r.cooldown
if isWithinBanPeriod || isWithinCooldown { if isWithinBanPeriod || isWithinCooldown {
@@ -161,13 +155,15 @@ func (r *Runner) shouldUpdateRecord(ctx context.Context, record librecords.Recor
r.logger.Warn(fmt.Sprintf("Skipping update for %s because %s address was not found", r.logger.Warn(fmt.Sprintf("Skipping update for %s because %s address was not found",
hostname, ipVersionToIPKind(ipVersion))) hostname, ipVersionToIPKind(ipVersion)))
return false return false
} else if publicIP.Is6() {
publicIP = ipv6WithSuffix(publicIP, record.Provider.IPv6Suffix())
} }
if record.Provider.Proxied() { if record.Provider.Proxied() {
lastIP := record.History.GetCurrentIP() // can be nil lastIP := record.History.GetCurrentIP() // can be nil
return r.shouldUpdateRecordNoLookup(hostname, ipVersion, lastIP, publicIP) return r.shouldUpdateRecordNoLookup(hostname, ipVersion, lastIP, publicIP)
} }
return r.shouldUpdateRecordWithLookup(ctx, hostname, ipVersion, publicIP, ipv6MaskBits) return r.shouldUpdateRecordWithLookup(ctx, hostname, ipVersion, publicIP)
} }
func (r *Runner) shouldUpdateRecordNoLookup(hostname string, ipVersion ipversion.IPVersion, func (r *Runner) shouldUpdateRecordNoLookup(hostname string, ipVersion ipversion.IPVersion,
@@ -181,8 +177,8 @@ func (r *Runner) shouldUpdateRecordNoLookup(hostname string, ipVersion ipversion
return false return false
} }
func (r *Runner) shouldUpdateRecordWithLookup(ctx context.Context, hostname string, ipVersion ipversion.IPVersion, func (r *Runner) shouldUpdateRecordWithLookup(ctx context.Context, hostname string,
publicIP netip.Addr, ipv6MaskBits uint8) (update bool) { ipVersion ipversion.IPVersion, publicIP netip.Addr) (update bool) {
const tries = 5 const tries = 5
recordIPv4, recordIPv6, err := r.lookupIPsResilient(ctx, hostname, tries) recordIPv4, recordIPv6, err := r.lookupIPsResilient(ctx, hostname, tries)
if err != nil { if err != nil {
@@ -195,10 +191,6 @@ func (r *Runner) shouldUpdateRecordWithLookup(ctx context.Context, hostname stri
fmt.Sprint(tries) + " tries: " + err.Error()) // update anyway fmt.Sprint(tries) + " tries: " + err.Error()) // update anyway
} }
if recordIPv6.IsValid() {
recordIPv6 = mustMaskIPv6(recordIPv6, ipv6MaskBits)
}
ipKind := ipVersionToIPKind(ipVersion) ipKind := ipVersionToIPKind(ipVersion)
recordIP := recordIPv4 recordIP := recordIPv4
if publicIP.Is6() { if publicIP.Is6() {
@@ -249,18 +241,18 @@ func setInitialUpToDateStatus(db Database, id uint, updateIP netip.Addr, now tim
return db.Update(id, record) return db.Update(id, record)
} }
func (r *Runner) updateNecessary(ctx context.Context, ipv6MaskBits uint8) (errors []error) { func (r *Runner) updateNecessary(ctx context.Context) (errors []error) {
records := r.db.SelectAll() records := r.db.SelectAll()
doIP, doIPv4, doIPv6 := doIPVersion(records) doIP, doIPv4, doIPv6 := doIPVersion(records)
r.logger.Debug(fmt.Sprintf("configured to fetch IP: v4 or v6: %t, v4: %t, v6: %t", doIP, doIPv4, doIPv6)) r.logger.Debug(fmt.Sprintf("configured to fetch IP: v4 or v6: %t, v4: %t, v6: %t", doIP, doIPv4, doIPv6))
ip, ipv4, ipv6, errors := r.getNewIPs(ctx, doIP, doIPv4, doIPv6, ipv6MaskBits) ip, ipv4, ipv6, errors := r.getNewIPs(ctx, doIP, doIPv4, doIPv6)
r.logger.Debug(fmt.Sprintf("your public IP address are: v4 or v6: %s, v4: %s, v6: %s", ip, ipv4, ipv6)) r.logger.Debug(fmt.Sprintf("your public IP address are: v4 or v6: %s, v4: %s, v6: %s", ip, ipv4, ipv6))
for _, err := range errors { for _, err := range errors {
r.logger.Error(err.Error()) r.logger.Error(err.Error())
} }
now := r.timeNow() now := r.timeNow()
recordIDs := r.getRecordIDsToUpdate(ctx, records, ip, ipv4, ipv6, now, ipv6MaskBits) recordIDs := r.getRecordIDsToUpdate(ctx, records, ip, ipv4, ipv6, now)
for i, record := range records { for i, record := range records {
id := uint(i) id := uint(i)
@@ -269,6 +261,10 @@ func (r *Runner) updateNecessary(ctx context.Context, ipv6MaskBits uint8) (error
continue continue
} }
updateIP := getIPMatchingVersion(ip, ipv4, ipv6, record.Provider.IPVersion()) updateIP := getIPMatchingVersion(ip, ipv4, ipv6, record.Provider.IPVersion())
if updateIP.Is6() {
updateIP = ipv6WithSuffix(updateIP, record.Provider.IPv6Suffix())
}
err := setInitialUpToDateStatus(r.db, id, updateIP, now) err := setInitialUpToDateStatus(r.db, id, updateIP, now)
if err != nil { if err != nil {
err = fmt.Errorf("setting initial up to date status: %w", err) err = fmt.Errorf("setting initial up to date status: %w", err)
@@ -284,6 +280,8 @@ func (r *Runner) updateNecessary(ctx context.Context, ipv6MaskBits uint8) (error
errors = append(errors, err) errors = append(errors, err)
r.logger.Error(err.Error()) r.logger.Error(err.Error())
continue continue
} else if updateIP.Is6() {
updateIP = ipv6WithSuffix(updateIP, record.Provider.IPv6Suffix())
} }
r.logger.Info("Updating record " + record.Provider.String() + " to use " + updateIP.String()) r.logger.Info("Updating record " + record.Provider.String() + " to use " + updateIP.String())
err := r.updater.Update(ctx, id, updateIP, r.timeNow()) err := r.updater.Update(ctx, id, updateIP, r.timeNow())
@@ -307,9 +305,9 @@ func (r *Runner) Run(ctx context.Context, done chan<- struct{}) {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
r.updateNecessary(ctx, r.ipv6MaskBits) r.updateNecessary(ctx)
case <-r.force: case <-r.force:
r.forceResult <- r.updateNecessary(ctx, r.ipv6MaskBits) r.forceResult <- r.updateNecessary(ctx)
case <-ctx.Done(): case <-ctx.Done():
ticker.Stop() ticker.Stop()
return return