Skip to content

Commit

Permalink
fix(ipv6): replace bad regex with custom IPv6 extract function
Browse files Browse the repository at this point in the history
- Fix HTTP IPv6 fetching invalid extraction result
- Affects IPv6 comparison for allinkl, dnsomatic, google, he and noip
  • Loading branch information
qdm12 committed Jun 13, 2023
1 parent 204be20 commit 954dffd
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 155 deletions.
5 changes: 3 additions & 2 deletions internal/settings/providers/allinkl/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/qdm12/ddns-updater/internal/settings/errors"
"github.com/qdm12/ddns-updater/internal/settings/headers"
"github.com/qdm12/ddns-updater/internal/settings/utils"
"github.com/qdm12/ddns-updater/pkg/ipextract"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
)

Expand Down Expand Up @@ -159,9 +160,9 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
}
var ips []netip.Addr
if ip.Is4() {
ips = utils.FindIPv4Addresses(s)
ips = ipextract.IPv4(s)
} else {
ips = utils.FindIPv6Addresses(s)
ips = ipextract.IPv6(s)
}

if len(ips) == 0 {
Expand Down
5 changes: 3 additions & 2 deletions internal/settings/providers/dnsomatic/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/qdm12/ddns-updater/internal/settings/errors"
"github.com/qdm12/ddns-updater/internal/settings/headers"
"github.com/qdm12/ddns-updater/internal/settings/utils"
"github.com/qdm12/ddns-updater/pkg/ipextract"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
)

Expand Down Expand Up @@ -164,9 +165,9 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add

var ips []netip.Addr
if ip.Is4() {
ips = utils.FindIPv4Addresses(s)
ips = ipextract.IPv4(s)
} else {
ips = utils.FindIPv6Addresses(s)
ips = ipextract.IPv6(s)
}

if len(ips) == 0 {
Expand Down
3 changes: 2 additions & 1 deletion internal/settings/providers/duckdns/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/qdm12/ddns-updater/internal/settings/errors"
"github.com/qdm12/ddns-updater/internal/settings/headers"
"github.com/qdm12/ddns-updater/internal/settings/utils"
"github.com/qdm12/ddns-updater/pkg/ipextract"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
)

Expand Down Expand Up @@ -143,7 +144,7 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add
case s[0:minChars] == "KO":
return netip.Addr{}, fmt.Errorf("%w", errors.ErrAuth)
case s[0:minChars] == "OK":
ips := utils.FindIPv4Addresses(s)
ips := ipextract.IPv4(s)
if len(ips) == 0 {
return netip.Addr{}, fmt.Errorf("%w", errors.ErrNoIPInResponse)
}
Expand Down
5 changes: 3 additions & 2 deletions internal/settings/providers/google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/qdm12/ddns-updater/internal/settings/errors"
"github.com/qdm12/ddns-updater/internal/settings/headers"
"github.com/qdm12/ddns-updater/internal/settings/utils"
"github.com/qdm12/ddns-updater/pkg/ipextract"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
)

Expand Down Expand Up @@ -152,9 +153,9 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add

var ips []netip.Addr
if ip.Is4() {
ips = utils.FindIPv4Addresses(s)
ips = ipextract.IPv4(s)
} else {
ips = utils.FindIPv6Addresses(s)
ips = ipextract.IPv6(s)
}

if len(ips) == 0 {
Expand Down
5 changes: 3 additions & 2 deletions internal/settings/providers/he/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/qdm12/ddns-updater/internal/settings/errors"
"github.com/qdm12/ddns-updater/internal/settings/headers"
"github.com/qdm12/ddns-updater/internal/settings/utils"
"github.com/qdm12/ddns-updater/pkg/ipextract"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
)

Expand Down Expand Up @@ -136,9 +137,9 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add

var ips []netip.Addr
if ip.Is4() {
ips = utils.FindIPv4Addresses(s)
ips = ipextract.IPv4(s)
} else {
ips = utils.FindIPv6Addresses(s)
ips = ipextract.IPv6(s)
}

if len(ips) == 0 {
Expand Down
5 changes: 3 additions & 2 deletions internal/settings/providers/noip/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/qdm12/ddns-updater/internal/settings/errors"
"github.com/qdm12/ddns-updater/internal/settings/headers"
"github.com/qdm12/ddns-updater/internal/settings/utils"
"github.com/qdm12/ddns-updater/pkg/ipextract"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
)

Expand Down Expand Up @@ -164,9 +165,9 @@ func (p *Provider) Update(ctx context.Context, client *http.Client, ip netip.Add

var ips []netip.Addr
if ip.Is4() {
ips = utils.FindIPv4Addresses(s)
ips = ipextract.IPv4(s)
} else {
ips = utils.FindIPv6Addresses(s)
ips = ipextract.IPv6(s)
}

if !p.useProviderIP && len(ips) == 0 {
Expand Down
28 changes: 0 additions & 28 deletions internal/settings/utils/regex.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,13 @@
package utils

import (
"net/netip"
"regexp"
)

var (
regexEmail = regexp.MustCompile(`[a-zA-Z0-9-_.+]+@[a-zA-Z0-9-_.]+\.[a-zA-Z]{2,10}`)
regexIPv4 = regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`)
regexIPv6 = regexp.MustCompile(`(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`) //nolint:lll
)

func MatchEmail(email string) bool {
return regexEmail.MatchString(email)
}

func FindIPv4Addresses(text string) (addresses []netip.Addr) {
const n = -1
ipv4Strings := regexIPv4.FindAllString(text, n)
return mustParseIPAddresses(ipv4Strings)
}

func FindIPv6Addresses(text string) (addresses []netip.Addr) {
const n = -1
ipv6Strings := regexIPv6.FindAllString(text, n)
return mustParseIPAddresses(ipv6Strings)
}

func mustParseIPAddresses(ipStrings []string) (addresses []netip.Addr) {
if len(ipStrings) == 0 {
return nil
}

addresses = make([]netip.Addr, len(ipStrings))
for i, ipString := range ipStrings {
addresses[i] = netip.MustParseAddr(ipString)
}

return addresses
}
80 changes: 0 additions & 80 deletions internal/settings/utils/regex_test.go

This file was deleted.

52 changes: 52 additions & 0 deletions pkg/ipextract/ipextract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ipextract

import (
"net/netip"
"strings"
)

// IPv4 extracts all valid IPv4 addresses from a given
// text string. Each IPv4 address must be separated by a character
// not part of the IPv4 alphabet (0123456789.).
// Performance-wise, this extraction is at least x3 times faster
// than using a regular expression.
func IPv4(text string) (addresses []netip.Addr) {
const ipv4Alphabet = "0123456789."
return extract(text, ipv4Alphabet)
}

// IPv6 extracts all valid IPv6 addresses from a given
// text string. Each IPv6 address must be separated by a character
// not part of the IPv6 alphabet (0123456789abcdefABCDEF:).
// Performance-wise, this extraction is at least x3 times faster
// than using a regular expression.
func IPv6(text string) (addresses []netip.Addr) {
const ipv6Alphabet = "0123456789abcdefABCDEF:"
return extract(text, ipv6Alphabet)
}

func extract(text string, alphabet string) (addresses []netip.Addr) {
var start, end int
for {
for i := start; i < len(text); i++ {
r := rune(text[i])
if !strings.ContainsRune(alphabet, r) {
break
}
end++
}

possibleIPString := text[start:end]
ipAddress, err := netip.ParseAddr(possibleIPString)
if err == nil { // Valid IP address found
addresses = append(addresses, ipAddress)
}

if end == len(text) {
return addresses
}

start = end + 1 // + 1 to skip non alphabet match character
end = start
}
}
Loading

0 comments on commit 954dffd

Please sign in to comment.