mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 07:40:56 +08:00 
			
		
		
		
	支持任意域名通过CNAME访问服务(开启选项后)
This commit is contained in:
		
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							@@ -22,6 +22,7 @@ require (
 | 
			
		||||
	github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
 | 
			
		||||
	github.com/lionsoul2014/ip2region v2.2.0-release+incompatible
 | 
			
		||||
	github.com/mattn/go-sqlite3 v2.0.3+incompatible
 | 
			
		||||
	github.com/miekg/dns v1.1.43
 | 
			
		||||
	github.com/mssola/user_agent v0.5.2
 | 
			
		||||
	github.com/pires/go-proxyproto v0.6.1
 | 
			
		||||
	github.com/shirou/gopsutil v3.21.5+incompatible
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.sum
									
									
									
									
									
								
							@@ -96,6 +96,8 @@ github.com/lionsoul2014/ip2region v2.2.0-release+incompatible h1:1qp9iks+69h7IGL
 | 
			
		||||
github.com/lionsoul2014/ip2region v2.2.0-release+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI=
 | 
			
		||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
 | 
			
		||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 | 
			
		||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
 | 
			
		||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/mssola/user_agent v0.5.2 h1:CZkTUahjL1+OcZ5zv3kZr8QiJ8jy2H08vZIEkBeRbxo=
 | 
			
		||||
@@ -163,6 +165,7 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 | 
			
		||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
 
 | 
			
		||||
@@ -21,19 +21,19 @@ type BaseListener struct {
 | 
			
		||||
	countActiveConnections int64 // 当前活跃的连接数
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 初始化
 | 
			
		||||
// Init 初始化
 | 
			
		||||
func (this *BaseListener) Init() {
 | 
			
		||||
	this.namedServers = map[string]*NamedServer{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 清除既有配置
 | 
			
		||||
// Reset 清除既有配置
 | 
			
		||||
func (this *BaseListener) Reset() {
 | 
			
		||||
	this.namedServersLocker.Lock()
 | 
			
		||||
	this.namedServers = map[string]*NamedServer{}
 | 
			
		||||
	this.namedServersLocker.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 获取当前活跃连接数
 | 
			
		||||
// CountActiveListeners 获取当前活跃连接数
 | 
			
		||||
func (this *BaseListener) CountActiveListeners() int {
 | 
			
		||||
	return types.Int(this.countActiveConnections)
 | 
			
		||||
}
 | 
			
		||||
@@ -253,3 +253,38 @@ func (this *BaseListener) findNamedServerMatched(name string) (serverConfig *ser
 | 
			
		||||
 | 
			
		||||
	return nil, name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 使用CNAME来查找服务
 | 
			
		||||
// TODO 防止单IP随机生成域名攻击
 | 
			
		||||
func (this *BaseListener) findServerWithCname(domain string) *serverconfigs.ServerConfig {
 | 
			
		||||
	if !sharedNodeConfig.SupportCNAME {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var realName = sharedCNAMEManager.Lookup(domain)
 | 
			
		||||
	if len(realName) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.serversLocker.Lock()
 | 
			
		||||
	defer this.serversLocker.Unlock()
 | 
			
		||||
 | 
			
		||||
	group := this.Group
 | 
			
		||||
	if group == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	currentServers := group.Servers
 | 
			
		||||
	countServers := len(currentServers)
 | 
			
		||||
	if countServers == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, server := range currentServers {
 | 
			
		||||
		if server.SupportCNAME && lists.ContainsString(server.AliasServerNames, realName) {
 | 
			
		||||
			return server
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -156,6 +156,8 @@ func (this *HTTPListener) handleHTTP(rawWriter http.ResponseWriter, rawReq *http
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server, serverName := this.findNamedServer(domain)
 | 
			
		||||
	if server == nil {
 | 
			
		||||
		server = this.findServerWithCname(domain)
 | 
			
		||||
		if server == nil {
 | 
			
		||||
			// 严格匹配域名模式下,我们拒绝用户访问
 | 
			
		||||
			if sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly {
 | 
			
		||||
@@ -184,6 +186,9 @@ func (this *HTTPListener) handleHTTP(rawWriter http.ResponseWriter, rawReq *http
 | 
			
		||||
 | 
			
		||||
			http.Error(rawWriter, "404 page not found: '"+rawReq.URL.String()+"'", http.StatusNotFound)
 | 
			
		||||
			return
 | 
			
		||||
		} else {
 | 
			
		||||
			serverName = domain
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 包装新请求对象
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								internal/nodes/server_cname_manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								internal/nodes/server_cname_manager.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package nodes
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/utils"
 | 
			
		||||
	"github.com/iwind/TeaGo/types"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var sharedCNAMEManager = NewServerCNAMEManager()
 | 
			
		||||
 | 
			
		||||
// ServerCNAMEManager 服务CNAME管理
 | 
			
		||||
// TODO 需要自动更新缓存里的记录
 | 
			
		||||
type ServerCNAMEManager struct {
 | 
			
		||||
	ttlCache *ttlcache.Cache
 | 
			
		||||
 | 
			
		||||
	locker sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewServerCNAMEManager() *ServerCNAMEManager {
 | 
			
		||||
	return &ServerCNAMEManager{
 | 
			
		||||
		ttlCache: ttlcache.NewCache(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *ServerCNAMEManager) Lookup(domain string) string {
 | 
			
		||||
	if len(domain) == 0 {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var item = this.ttlCache.Read(domain)
 | 
			
		||||
	if item != nil {
 | 
			
		||||
		return types.String(item.Value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cname, _ := utils.LookupCNAME(domain)
 | 
			
		||||
	if len(cname) > 0 {
 | 
			
		||||
		cname = strings.TrimSuffix(cname, ".")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.ttlCache.Write(domain, cname, time.Now().Unix()+600)
 | 
			
		||||
 | 
			
		||||
	return cname
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								internal/nodes/server_cname_manager_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								internal/nodes/server_cname_manager_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package nodes
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestServerCNameManager_Lookup(t *testing.T) {
 | 
			
		||||
	var cnameManager = NewServerCNAMEManager()
 | 
			
		||||
	t.Log(cnameManager.Lookup("www.yun4s.cn"))
 | 
			
		||||
 | 
			
		||||
	var before = time.Now()
 | 
			
		||||
	defer func() {
 | 
			
		||||
		t.Log(time.Since(before).Seconds()*1000, "ms")
 | 
			
		||||
	}()
 | 
			
		||||
	t.Log(cnameManager.Lookup("www.yun4s.cn"))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								internal/utils/lookup.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								internal/utils/lookup.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
 | 
			
		||||
	"github.com/miekg/dns"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LookupCNAME 获取CNAME
 | 
			
		||||
func LookupCNAME(host string) (string, error) {
 | 
			
		||||
	config, err := dns.ClientConfigFromFile("/etc/resolv.conf")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := new(dns.Client)
 | 
			
		||||
	m := new(dns.Msg)
 | 
			
		||||
 | 
			
		||||
	m.SetQuestion(host+".", dns.TypeCNAME)
 | 
			
		||||
	m.RecursionDesired = true
 | 
			
		||||
 | 
			
		||||
	var lastErr error
 | 
			
		||||
	for _, serverAddr := range config.Servers {
 | 
			
		||||
		r, _, err := c.Exchange(m, configutils.QuoteIP(serverAddr)+":"+config.Port)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			lastErr = err
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if len(r.Answer) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return r.Answer[0].(*dns.CNAME).Target, nil
 | 
			
		||||
	}
 | 
			
		||||
	return "", lastErr
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								internal/utils/lookup_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								internal/utils/lookup_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
			
		||||
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import "testing"
 | 
			
		||||
 | 
			
		||||
func TestLookupCNAME(t *testing.T) {
 | 
			
		||||
	t.Log(LookupCNAME("www.yun4s.cn"))
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user