Golang匹配域名外卡

时间:2017-10-11 08:58:25

标签: regex go subdomain wildcard

我有hostnameWhitelist地图

C:/fakepath/myfilename.txt

我检查是否允许传入请求的主机名的方式是 -

var hostnameWhitelist = map[string] bool { "test.mydomain.com":true, "test12.mydomaindev.com":true}

虽然这很有效,但我如何进行像 -

这样的外卡匹配
    url, errURL := url.Parse("test.mydomain.com")
    if errURL != nil {
        fmt.Println("error during parsing URL")
        return false
    }
    fmt.Println("HOSTNAME = " + url.Hostname())

    if ok := hostnameWhitelist[url.Hostname()]; ok {
        fmt.Println("valid domain, allow access")
    } else {
        fmt.Println("NOT valid domain")
        return false
    }

这两个都应该通过。

虽然

*.mydomain.com 
*.mydomaindev.com 

应该失败

5 个答案:

答案 0 :(得分:2)

您可以使用* .domain.com

格式存储地图的键

使用strings.SplitAfterNstrings.Join将您获得的所有主机名转换为该格式。

split := strings.SplitAfterN(url.Hostname(),".",2)
split[0] = "*"
hostName := strings.Join(split,".")
...
hostnameWhitelist[hostName]
...

Play Link

无关的改进

如果您将地图纯粹用作白名单,则可以使用map[string]struct{}代替map[string]bool。但正如Peter在他的评论中提到的那样,只有当你拥有一个非常大的白名单时它才有意义。

答案 1 :(得分:1)

正则表达式是您解决问题的方法,map [string] bool可能无法正常工作,因为您尝试将正则表达式与单个值匹配。

package main

import (
    "fmt"
    "regexp"
)

func main() {
    if matched, _ := regexp.MatchString(".*\\.?mydomain.*", "mydomaindev.com"); matched {
        fmt.Println("Valid domain")
    }
}

这将匹配所有域与模式mydomain,因此www.mydomain.com www.mydomaindev.com将匹配byt test.com并且hello.com将失败

其他方便的字符串操作是,

//This would match a.mydomain.com, b.mydomain.com etc.,
if strings.HasSuffix(url1.Hostname(), ".mydomain.com") {
    fmt.Println("Valid domain allow access")
}

//To match anything with mydomain - like mydomain.com, mydomaindev.com
if strings.Contains(url2.Hostname(), "mydomain.com") {
    fmt.Println("Valid domain allow access")
}

答案 2 :(得分:1)

在开始时与通配符进行通配符匹配非常昂贵。正则表达式在性能方面可能很困难,具体取决于数据集的大小和针对数据集进行评估的速度。您可以尝试使用后缀树,但是我怀疑性能可能会成为问题(我尚未在数据中对其进行测试)。

我们使用的一种方法是构建一个Radix Trie(紧凑前缀trie),其签名域名的标签按相反的八位字节顺序排列。您的签名域*.foo.example.com变为com.example.foo.*,这将通配符放在末尾。然后,自定义构建的Radix树只有到达通配符节点时才需要停止匹配。您的Trie可以支持完全字符串匹配和通配符匹配。如果您希望允许通配符位于域名中间,那么性能可能会成为问题。

使用Trie评估域名的最大挑战之一不是搜索时间,而是内存消耗,以及当您有很多签名时启动程序需要多长时间。

我们评估了一些实现(主要是在开始时没有通配符支持的情况下),用于测试加载时间,分配,内部节点数量,内存消耗,GC时间以及搜索/插入/删除时间。

我们测试过的实现方式

很明显,使用golang映射将提供最佳性能,但是当需要检索(因此使用Trie这个词)时,例如来自数据集的前缀信息,golang映射无法提供我们所需的功能。

我们在Trie中保留了大约70万个域名签名。构建时间为2秒,300MB内存,500万个分配,2秒GC和搜索成本为150ns / op。

如果我们对相同的签名使用golang映射(不使用通配符),则会获得0.5秒的加载时间,50MB的内存,可忽略的分配,1.6秒的GC和25ns / op的搜索成本。

在我们最初的实现中,构建时间为6秒,1GB内存,6000万个分配,5秒的GC和约200 ns / op的搜索成本。

从这些结果可以看出,我们设法降低了内存消耗和加载时间,而搜索成本却保持不变。

如果您要进行CIDR匹配,建议您检出https://github.com/kentik/patricia。为了减少GC时间,它可以避免指针。

祝你工作顺利!

答案 3 :(得分:0)

如果您希望在您的域中拥有多个深度,例如:

  • *。foo.example.org
  • *。example.com

然后我会为通配符添加第二个容器:

endColor

然后,检查您的测试域是否以其中一个条目结束:

var wdomains = []string { ".foo.example.org", ".example.com"}

注意:如果您有超过数百个域,则可以使用基数树。

https://play.golang.org/p/-4n8mlGmpH

答案 4 :(得分:0)

您可以像使用 Set 数据结构一样使用 fstest.MapFS 全局匹配:

package main
import "testing/fstest"

var tests = []struct {
   pat string
   res int
} {
   {"*.hello.com", 0},
   {"*.mydomain.com", 1},
   {"*.mydomaindev.com", 1},
   {"*.test.com", 0},
}

func main() {
   m := fstest.MapFS{"test.mydomain.com": nil, "test12.mydomaindev.com": nil}
   for _, test := range tests {
      a, e := m.Glob(test.pat)
      if e != nil {
         panic(e)
      }
      if len(a) != test.res {
         panic(len(a))
      }
   }
}

https://golang.org/pkg/testing/fstest