Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
4.2 KiB
159 lines
4.2 KiB
// Copyright 2021 The Gitea Authors. All rights reserved. |
|
// Use of this source code is governed by a MIT-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package hostmatcher |
|
|
|
import ( |
|
"net" |
|
"path/filepath" |
|
"strings" |
|
|
|
"code.gitea.io/gitea/modules/util" |
|
) |
|
|
|
// HostMatchList is used to check if a host or IP is in a list. |
|
type HostMatchList struct { |
|
SettingKeyHint string |
|
SettingValue string |
|
|
|
// builtins networks |
|
builtins []string |
|
// patterns for host names (with wildcard support) |
|
patterns []string |
|
// ipNets is the CIDR network list |
|
ipNets []*net.IPNet |
|
} |
|
|
|
// MatchBuiltinExternal A valid non-private unicast IP, all hosts on public internet are matched |
|
const MatchBuiltinExternal = "external" |
|
|
|
// MatchBuiltinPrivate RFC 1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and RFC 4193 (FC00::/7). Also called LAN/Intranet. |
|
const MatchBuiltinPrivate = "private" |
|
|
|
// MatchBuiltinLoopback 127.0.0.0/8 for IPv4 and ::1/128 for IPv6, localhost is included. |
|
const MatchBuiltinLoopback = "loopback" |
|
|
|
func isBuiltin(s string) bool { |
|
return s == MatchBuiltinExternal || s == MatchBuiltinPrivate || s == MatchBuiltinLoopback |
|
} |
|
|
|
// ParseHostMatchList parses the host list HostMatchList |
|
func ParseHostMatchList(settingKeyHint, hostList string) *HostMatchList { |
|
hl := &HostMatchList{SettingKeyHint: settingKeyHint, SettingValue: hostList} |
|
for _, s := range strings.Split(hostList, ",") { |
|
s = strings.ToLower(strings.TrimSpace(s)) |
|
if s == "" { |
|
continue |
|
} |
|
_, ipNet, err := net.ParseCIDR(s) |
|
if err == nil { |
|
hl.ipNets = append(hl.ipNets, ipNet) |
|
} else if isBuiltin(s) { |
|
hl.builtins = append(hl.builtins, s) |
|
} else { |
|
hl.patterns = append(hl.patterns, s) |
|
} |
|
} |
|
return hl |
|
} |
|
|
|
// ParseSimpleMatchList parse a simple matchlist (no built-in networks, no CIDR support, only wildcard pattern match) |
|
func ParseSimpleMatchList(settingKeyHint, matchList string) *HostMatchList { |
|
hl := &HostMatchList{ |
|
SettingKeyHint: settingKeyHint, |
|
SettingValue: matchList, |
|
} |
|
for _, s := range strings.Split(matchList, ",") { |
|
s = strings.ToLower(strings.TrimSpace(s)) |
|
if s == "" { |
|
continue |
|
} |
|
// we keep the same result as old `matchlist`, so no builtin/CIDR support here, we only match wildcard patterns |
|
hl.patterns = append(hl.patterns, s) |
|
} |
|
return hl |
|
} |
|
|
|
// AppendBuiltin appends more builtins to match |
|
func (hl *HostMatchList) AppendBuiltin(builtin string) { |
|
hl.builtins = append(hl.builtins, builtin) |
|
} |
|
|
|
// IsEmpty checks if the checklist is empty |
|
func (hl *HostMatchList) IsEmpty() bool { |
|
return hl == nil || (len(hl.builtins) == 0 && len(hl.patterns) == 0 && len(hl.ipNets) == 0) |
|
} |
|
|
|
func (hl *HostMatchList) checkPattern(host string) bool { |
|
host = strings.ToLower(strings.TrimSpace(host)) |
|
for _, pattern := range hl.patterns { |
|
if matched, _ := filepath.Match(pattern, host); matched { |
|
return true |
|
} |
|
} |
|
return false |
|
} |
|
|
|
func (hl *HostMatchList) checkIP(ip net.IP) bool { |
|
for _, pattern := range hl.patterns { |
|
if pattern == "*" { |
|
return true |
|
} |
|
} |
|
for _, builtin := range hl.builtins { |
|
switch builtin { |
|
case MatchBuiltinExternal: |
|
if ip.IsGlobalUnicast() && !util.IsIPPrivate(ip) { |
|
return true |
|
} |
|
case MatchBuiltinPrivate: |
|
if util.IsIPPrivate(ip) { |
|
return true |
|
} |
|
case MatchBuiltinLoopback: |
|
if ip.IsLoopback() { |
|
return true |
|
} |
|
} |
|
} |
|
for _, ipNet := range hl.ipNets { |
|
if ipNet.Contains(ip) { |
|
return true |
|
} |
|
} |
|
return false |
|
} |
|
|
|
// MatchHostName checks if the host matches an allow/deny(block) list |
|
func (hl *HostMatchList) MatchHostName(host string) bool { |
|
hostname, _, err := net.SplitHostPort(host) |
|
if err != nil { |
|
hostname = host |
|
} |
|
|
|
if hl == nil { |
|
return false |
|
} |
|
if hl.checkPattern(hostname) { |
|
return true |
|
} |
|
if ip := net.ParseIP(hostname); ip != nil { |
|
return hl.checkIP(ip) |
|
} |
|
return false |
|
} |
|
|
|
// MatchIPAddr checks if the IP matches an allow/deny(block) list, it's safe to pass `nil` to `ip` |
|
func (hl *HostMatchList) MatchIPAddr(ip net.IP) bool { |
|
if hl == nil { |
|
return false |
|
} |
|
host := ip.String() // nil-safe, we will get "<nil>" if ip is nil |
|
return hl.checkPattern(host) || hl.checkIP(ip) |
|
} |
|
|
|
// MatchHostOrIP checks if the host or IP matches an allow/deny(block) list |
|
func (hl *HostMatchList) MatchHostOrIP(host string, ip net.IP) bool { |
|
return hl.MatchHostName(host) || hl.MatchIPAddr(ip) |
|
}
|
|
|