You've already forked ddns-updater
Merge pull request #8 from mkelcik/new-1_1_1_1_resolver
Add 1.1.1.1 resolver
This commit is contained in:
@@ -17,7 +17,7 @@ Before run, you need configure this environment variables.
|
|||||||
- `CLOUDFLARE_ZONE` - (required) zone name with domain you want to check. See: [https://developers.cloudflare.com/fundamentals/get-started/concepts/accounts-and-zones/#zones](https://developers.cloudflare.com/fundamentals/get-started/concepts/accounts-and-zones/#zones)
|
- `CLOUDFLARE_ZONE` - (required) zone name with domain you want to check. See: [https://developers.cloudflare.com/fundamentals/get-started/concepts/accounts-and-zones/#zones](https://developers.cloudflare.com/fundamentals/get-started/concepts/accounts-and-zones/#zones)
|
||||||
- `ON_CHANGE_COMMENT` - (optional) in the event that the ip address of the dns record changes, this comment will be added to the record
|
- `ON_CHANGE_COMMENT` - (optional) in the event that the ip address of the dns record changes, this comment will be added to the record
|
||||||
- `CHECK_INTERVAL_SECONDS` - (optional) how often will the ip address of the records be checked (default: `300`)
|
- `CHECK_INTERVAL_SECONDS` - (optional) how often will the ip address of the records be checked (default: `300`)
|
||||||
- `PUBLIC_IP_RESOLVER` - (optional) public ip address resolver. (default: `ifconfig.me`) Available: `ifconfig.me`, `v4.ident.me`
|
- `PUBLIC_IP_RESOLVER` - (optional) public ip address resolver. (default: `ifconfig.me`) Available: `ifconfig.me`, `v4.ident.me`, `1.1.1.1`
|
||||||
|
|
||||||
### Building from source
|
### Building from source
|
||||||
|
|
||||||
|
|||||||
4
main.go
4
main.go
@@ -20,6 +20,8 @@ type PublicIpResolver interface {
|
|||||||
func getResolver(resolverName string) (PublicIpResolver, string) {
|
func getResolver(resolverName string) (PublicIpResolver, string) {
|
||||||
switch resolverName {
|
switch resolverName {
|
||||||
// HERE add another resolver if needed
|
// HERE add another resolver if needed
|
||||||
|
case public_resolvers.CloudflareTraceTag:
|
||||||
|
return public_resolvers.NewDefaultCloudflareTrace(), public_resolvers.CloudflareTraceTag
|
||||||
case public_resolvers.V4IdentMeTag:
|
case public_resolvers.V4IdentMeTag:
|
||||||
return public_resolvers.NewV4IdentMeDefault(), public_resolvers.V4IdentMeTag
|
return public_resolvers.NewV4IdentMeDefault(), public_resolvers.V4IdentMeTag
|
||||||
case public_resolvers.IfConfigMeTag:
|
case public_resolvers.IfConfigMeTag:
|
||||||
@@ -57,7 +59,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
log.Printf("Current public ip `%s` (%s)", currentPublicIP, resolverTag)
|
log.Printf("Current public ip `%s` (resolver: %s)", currentPublicIP, resolverTag)
|
||||||
|
|
||||||
dns, err := allDNSRecords(ctx, api, cloudflare.ZoneIdentifier(zoneID))
|
dns, err := allDNSRecords(ctx, api, cloudflare.ZoneIdentifier(zoneID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,19 +2,30 @@ package public_resolvers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var NoIPInResponseError = errors.New("no ip found in response")
|
||||||
|
|
||||||
type Doer interface {
|
type Doer interface {
|
||||||
Do(*http.Request) (*http.Response, error)
|
Do(*http.Request) (*http.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ipParserFunc func(reader io.Reader) (string, error)
|
||||||
|
|
||||||
|
func defaultIpParser(reader io.Reader) (string, error) {
|
||||||
|
out, err := io.ReadAll(reader)
|
||||||
|
return string(out), err
|
||||||
|
}
|
||||||
|
|
||||||
type baseResolver struct {
|
type baseResolver struct {
|
||||||
client Doer
|
client Doer
|
||||||
url string
|
url string
|
||||||
|
ipParser ipParserFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i baseResolver) ResolvePublicIp(ctx context.Context) (net.IP, error) {
|
func (i baseResolver) ResolvePublicIp(ctx context.Context) (net.IP, error) {
|
||||||
@@ -35,10 +46,10 @@ func (i baseResolver) ResolvePublicIp(ctx context.Context) (net.IP, error) {
|
|||||||
return net.IP{}, fmt.Errorf("unexpected response code %d", resp.StatusCode)
|
return net.IP{}, fmt.Errorf("unexpected response code %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
ipText, err := io.ReadAll(resp.Body)
|
ipText, err := i.ipParser(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return net.IP{}, fmt.Errorf("error reading body: %w", err)
|
return net.IP{}, fmt.Errorf("error reading body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return net.ParseIP(string(ipText)), nil
|
return net.ParseIP(ipText), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||||||
// NewTestClient returns *http.Client with Transport replaced to avoid making real calls
|
// NewTestClient returns *http.Client with Transport replaced to avoid making real calls
|
||||||
func NewTestClient(fn RoundTripFunc) *http.Client {
|
func NewTestClient(fn RoundTripFunc) *http.Client {
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: RoundTripFunc(fn),
|
Transport: fn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@ func Test_baseResolver_ResolvePublicIp(t *testing.T) {
|
|||||||
type fields struct {
|
type fields struct {
|
||||||
client Doer
|
client Doer
|
||||||
url string
|
url string
|
||||||
|
fn ipParserFunc
|
||||||
}
|
}
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -70,6 +71,7 @@ func Test_baseResolver_ResolvePublicIp(t *testing.T) {
|
|||||||
fields: fields{
|
fields: fields{
|
||||||
client: client,
|
client: client,
|
||||||
url: testUrl,
|
url: testUrl,
|
||||||
|
fn: defaultIpParser,
|
||||||
},
|
},
|
||||||
args: args{
|
args: args{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@@ -81,8 +83,9 @@ func Test_baseResolver_ResolvePublicIp(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
i := baseResolver{
|
i := baseResolver{
|
||||||
client: tt.fields.client,
|
client: tt.fields.client,
|
||||||
url: tt.fields.url,
|
url: tt.fields.url,
|
||||||
|
ipParser: tt.fields.fn,
|
||||||
}
|
}
|
||||||
got, err := i.ResolvePublicIp(tt.args.ctx)
|
got, err := i.ResolvePublicIp(tt.args.ctx)
|
||||||
if (err != nil) != tt.wantErr {
|
if (err != nil) != tt.wantErr {
|
||||||
|
|||||||
49
public_resolvers/cloudflare_trace.go
Normal file
49
public_resolvers/cloudflare_trace.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package public_resolvers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CloudflareTraceTag = "1.1.1.1"
|
||||||
|
CloudflareTraceUrl = "https://1.1.1.1/cdn-cgi/trace"
|
||||||
|
|
||||||
|
ipPrefix = "ip="
|
||||||
|
)
|
||||||
|
|
||||||
|
type CloudflareTrace struct {
|
||||||
|
baseResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultCloudflareTrace() *CloudflareTrace {
|
||||||
|
return NewCloudflareTrace(&http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloudflareTraceResponseParser(reader io.Reader) (string, error) {
|
||||||
|
data, err := io.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range strings.Split(string(data), "\n") {
|
||||||
|
if strings.Index(row, ipPrefix) == 0 {
|
||||||
|
return strings.TrimSpace(strings.ReplaceAll(row, ipPrefix, "")), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", NoIPInResponseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCloudflareTrace(client Doer) *CloudflareTrace {
|
||||||
|
return &CloudflareTrace{
|
||||||
|
baseResolver: baseResolver{
|
||||||
|
client: client,
|
||||||
|
url: CloudflareTraceUrl,
|
||||||
|
ipParser: cloudflareTraceResponseParser,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
78
public_resolvers/cloudflare_trace_test.go
Normal file
78
public_resolvers/cloudflare_trace_test.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package public_resolvers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_cloudflareTraceResponseParser(t *testing.T) {
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
reader io.Reader
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ok",
|
||||||
|
args: args{
|
||||||
|
reader: bytes.NewBuffer([]byte(`fl=31f118
|
||||||
|
h=1.1.1.1
|
||||||
|
ip=94.113.142.206
|
||||||
|
ts=1683145336.383
|
||||||
|
visit_scheme=https
|
||||||
|
uag=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
|
||||||
|
colo=PRG
|
||||||
|
sliver=none
|
||||||
|
http=http/2
|
||||||
|
loc=CZ
|
||||||
|
tls=TLSv1.3
|
||||||
|
sni=off
|
||||||
|
warp=off
|
||||||
|
gateway=off
|
||||||
|
rbi=off
|
||||||
|
kex=X25519`)),
|
||||||
|
},
|
||||||
|
want: "94.113.142.206",
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no ip in response",
|
||||||
|
args: args{
|
||||||
|
reader: bytes.NewBuffer([]byte(`fl=31f118
|
||||||
|
h=1.1.1.1
|
||||||
|
ts=1683145336.383
|
||||||
|
visit_scheme=https
|
||||||
|
uag=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
|
||||||
|
colo=PRG
|
||||||
|
sliver=none
|
||||||
|
http=http/2
|
||||||
|
loc=CZ
|
||||||
|
tls=TLSv1.3
|
||||||
|
sni=off
|
||||||
|
warp=off
|
||||||
|
gateway=off
|
||||||
|
rbi=off
|
||||||
|
kex=X25519`)),
|
||||||
|
},
|
||||||
|
want: "",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := cloudflareTraceResponseParser(tt.args.reader)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("cloudflareTraceResponseParser() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("cloudflareTraceResponseParser() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,8 +23,9 @@ func NewDefaultIfConfigMe() *IfConfigMe {
|
|||||||
func NewIfConfigMe(client Doer) *IfConfigMe {
|
func NewIfConfigMe(client Doer) *IfConfigMe {
|
||||||
return &IfConfigMe{
|
return &IfConfigMe{
|
||||||
baseResolver: baseResolver{
|
baseResolver: baseResolver{
|
||||||
client: client,
|
client: client,
|
||||||
url: ifConfigMeUrl,
|
url: ifConfigMeUrl,
|
||||||
|
ipParser: defaultIpParser,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ func NewV4IdentMeDefault() *V4IdentMe {
|
|||||||
func NewV4IdentMe(client Doer) *V4IdentMe {
|
func NewV4IdentMe(client Doer) *V4IdentMe {
|
||||||
return &V4IdentMe{
|
return &V4IdentMe{
|
||||||
baseResolver: baseResolver{
|
baseResolver: baseResolver{
|
||||||
client: client,
|
client: client,
|
||||||
url: v4IdentMeUrl,
|
url: v4IdentMeUrl,
|
||||||
|
ipParser: defaultIpParser,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user