Skip to content
Permalink
Browse files

(path): Fix predefined regex order bug

Signed-off-by: Erfan Besharat <erbesharat@gmail.com>
  • Loading branch information
erbesharat committed Mar 10, 2020
1 parent c3e2644 commit 717fd4da59ed76f83e81e5c7fd42390b870a7551
Showing with 39 additions and 29 deletions.
  1. +2 −2 e2e/path/main.go
  2. +2 −2 e2e/path/zonefile
  3. +31 −25 path.go
  4. +4 −0 record.go
@@ -42,15 +42,15 @@ var tests = []test{
host: "predefined-regex.path.path.example.com",
path: "/test1",
},
expected: "https://predefined-redirect.host.path.example.com/first/test1",
expected: "https://predefined-redirect.host.path.example.com/second/test1",
},
{
name: "Redirect a path record using predefined regex records",
args: data{
host: "predefined-regex.path.path.example.com",
path: "/test1/test2",
},
expected: "https://predefined-redirect.host.path.example.com/second/test1/test2",
expected: "https://predefined-redirect.host.path.example.com/first/test1/test2",
},
{
name: "Fallback to \"to=\" when wildcard not found",
@@ -23,10 +23,10 @@ predefined-regex.path.path.example.com. IN A 172.20.10.2
_redirect.predefined-regex.path.path.example.com. IN TXT "v=txtv0;re=record;to=https://regex-redirect.path.path.example.com;type=path"

1.predefined-regex.path.path.example.com. IN A 172.20.10.2
_redirect.1.predefined-regex.path.path.example.com. IN TXT "v=txtv0;re=\\/test1;to=https://predefined-redirect.host.path.example.com/first{1};type=host"
_redirect.1.predefined-regex.path.path.example.com. IN TXT "v=txtv0;re=\\/test1\\/test2;to=https://predefined-redirect.host.path.example.com/first{1};type=host"

2.predefined-regex.path.path.example.com. IN A 172.20.10.2
_redirect.2.predefined-regex.path.path.example.com. IN TXT "v=txtv0;re=\\/test1\\/test2;to=https://predefined-redirect.host.path.example.com/second{1};type=host"
_redirect.2.predefined-regex.path.path.example.com. IN TXT "v=txtv0;re=\\/test1;to=https://predefined-redirect.host.path.example.com/second{1};type=host"

path.path.example.com. IN A 172.20.10.2
_redirect.path.path.example.com. IN TXT "v=txtv0;to=https://fallback-to.path.path.example.com;type=path;code=302"
56 path.go
@@ -34,12 +34,9 @@ type Path struct {
rec record
}

// RegexRecords connects each zone address to a RegexRecord
// which contains raw TXT record and the re= field
type RegexRecords map[string]RegexRecord

// RegexRecord holds the TXT record and re= field of a predefined regex record
type RegexRecord struct {
Position int
TXT string
Regex string
Submatches []string
@@ -118,6 +115,7 @@ func (p *Path) SpecificRecord() (*record, error) {
return nil, err
}

// Find the most specific regex
record, err := p.specificMatch(regexes)
if err != nil {
return nil, err
@@ -126,44 +124,46 @@ func (p *Path) SpecificRecord() (*record, error) {
return record, nil
}

func (p *Path) specificMatch(regexes RegexRecords) (*record, error) {
recordMatch := make(map[int]RegexRecord)

func (p *Path) specificMatch(regexes []RegexRecord) (*record, error) {
var specificZone RegexRecord
// Run each regex on the path and list them in a map
for _, zone := range regexes {
// Compile the regex and find the path submatches
regex, err := regexp.Compile(zone.Regex)
if err != nil {
return nil, fmt.Errorf("Couldn't compile the regex: %s", err.Error())
}
matches := regex.FindAllStringSubmatch(p.path, -1)
if len(matches) > 0 {
zone.Submatches = matches[0]

if len(matches) == 0 {
continue
}
recordMatch[len(zone.Submatches)] = zone
}

// Sort the map keys to find the most specific match
var keys []int
for k := range recordMatch {
keys = append(keys, k)
zone.Submatches = matches[0]

// Use the next regex if it has more matches
if len(zone.Submatches) > len(specificZone.Submatches) {
specificZone = zone
}
}
sort.Ints(keys)

// Add the most specific match's path slice to the request context to use in placeholders
*p.req = *p.req.WithContext(context.WithValue(p.req.Context(), "regexMatches", recordMatch[keys[len(keys)-1]].Submatches))
*p.req = *p.req.WithContext(context.WithValue(p.req.Context(), "regexMatches", specificZone.Submatches))

// Parse the specific regex record
var rec record
var err error
if rec, err = ParseRecord(recordMatch[keys[len(keys)-1]].TXT, p.rw, p.req, p.c); err != nil {
if rec, err = ParseRecord(specificZone.TXT, p.rw, p.req, p.c); err != nil {
return nil, fmt.Errorf("Could not parse record: %s", err)
}

return &rec, nil
}

func (p *Path) fetchRegexes() (RegexRecords, error) {
regexes := make(RegexRecords)
func (p *Path) fetchRegexes() ([]RegexRecord, error) {
regexes := []RegexRecord{}
for i, loop := 1, true; loop != false; i++ {
// Send a DNS query to each predefined regex record
txts, err := query(fmt.Sprintf("%d.%s", i, UpstreamZone(p.req)), p.req.Context(), p.c)
if err != nil && len(regexes) >= 1 {
break
@@ -172,17 +172,23 @@ func (p *Path) fetchRegexes() (RegexRecords, error) {
return nil, fmt.Errorf("Couldn't fetch the subzones for predefined regex: %s", err.Error())
}

// All predefined regex records should contain the re= field (even non-path records)
if !strings.Contains(txts[0], "re=") {
return nil, fmt.Errorf("Couldn't find the re= field in records: %s", err.Error())
}

// Extract the re= field from record and add it to the map
regexes[fmt.Sprintf("%d.%s", i, UpstreamZone(p.req))] = RegexRecord{
TXT: txts[0],
Regex: strings.TrimPrefix(strings.Split(txts[0][strings.Index(txts[0], "re="):], ";")[0], "re="),
}
// Extract the re= field from record and add it to the regex slice
regexes = append(regexes, RegexRecord{
Position: i,
TXT: txts[0],
Regex: strings.TrimPrefix(strings.Split(txts[0][strings.Index(txts[0], "re="):], ";")[0], "re="),
})
}

sort.Slice(regexes, func(i, j int) bool {
return regexes[i].Position < regexes[j].Position
})

return regexes, nil
}

@@ -128,6 +128,10 @@ func ParseRecord(str string, w http.ResponseWriter, req *http.Request, c Config)

case strings.HasPrefix(l, "root="):
l = strings.TrimPrefix(l, "root=")
l, err := parsePlaceholders(l, req, []string{})
if err != nil {
return record{}, err
}
l = ParseURI(l, w, req, c)
r.Root = l

0 comments on commit 717fd4d

Please sign in to comment.
You can’t perform that action at this time.