code.gitea.io/gitea@v1.19.3/modules/hostmatcher/hostmatcher.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package hostmatcher 5 6 import ( 7 "net" 8 "path/filepath" 9 "strings" 10 ) 11 12 // HostMatchList is used to check if a host or IP is in a list. 13 type HostMatchList struct { 14 SettingKeyHint string 15 SettingValue string 16 17 // builtins networks 18 builtins []string 19 // patterns for host names (with wildcard support) 20 patterns []string 21 // ipNets is the CIDR network list 22 ipNets []*net.IPNet 23 } 24 25 // MatchBuiltinExternal A valid non-private unicast IP, all hosts on public internet are matched 26 const MatchBuiltinExternal = "external" 27 28 // 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. 29 const MatchBuiltinPrivate = "private" 30 31 // MatchBuiltinLoopback 127.0.0.0/8 for IPv4 and ::1/128 for IPv6, localhost is included. 32 const MatchBuiltinLoopback = "loopback" 33 34 func isBuiltin(s string) bool { 35 return s == MatchBuiltinExternal || s == MatchBuiltinPrivate || s == MatchBuiltinLoopback 36 } 37 38 // ParseHostMatchList parses the host list HostMatchList 39 func ParseHostMatchList(settingKeyHint, hostList string) *HostMatchList { 40 hl := &HostMatchList{SettingKeyHint: settingKeyHint, SettingValue: hostList} 41 for _, s := range strings.Split(hostList, ",") { 42 s = strings.ToLower(strings.TrimSpace(s)) 43 if s == "" { 44 continue 45 } 46 _, ipNet, err := net.ParseCIDR(s) 47 if err == nil { 48 hl.ipNets = append(hl.ipNets, ipNet) 49 } else if isBuiltin(s) { 50 hl.builtins = append(hl.builtins, s) 51 } else { 52 hl.patterns = append(hl.patterns, s) 53 } 54 } 55 return hl 56 } 57 58 // ParseSimpleMatchList parse a simple matchlist (no built-in networks, no CIDR support, only wildcard pattern match) 59 func ParseSimpleMatchList(settingKeyHint, matchList string) *HostMatchList { 60 hl := &HostMatchList{ 61 SettingKeyHint: settingKeyHint, 62 SettingValue: matchList, 63 } 64 for _, s := range strings.Split(matchList, ",") { 65 s = strings.ToLower(strings.TrimSpace(s)) 66 if s == "" { 67 continue 68 } 69 // we keep the same result as old `matchlist`, so no builtin/CIDR support here, we only match wildcard patterns 70 hl.patterns = append(hl.patterns, s) 71 } 72 return hl 73 } 74 75 // AppendBuiltin appends more builtins to match 76 func (hl *HostMatchList) AppendBuiltin(builtin string) { 77 hl.builtins = append(hl.builtins, builtin) 78 } 79 80 // AppendPattern appends more pattern to match 81 func (hl *HostMatchList) AppendPattern(pattern string) { 82 hl.patterns = append(hl.patterns, pattern) 83 } 84 85 // IsEmpty checks if the checklist is empty 86 func (hl *HostMatchList) IsEmpty() bool { 87 return hl == nil || (len(hl.builtins) == 0 && len(hl.patterns) == 0 && len(hl.ipNets) == 0) 88 } 89 90 func (hl *HostMatchList) checkPattern(host string) bool { 91 host = strings.ToLower(strings.TrimSpace(host)) 92 for _, pattern := range hl.patterns { 93 if matched, _ := filepath.Match(pattern, host); matched { 94 return true 95 } 96 } 97 return false 98 } 99 100 func (hl *HostMatchList) checkIP(ip net.IP) bool { 101 for _, pattern := range hl.patterns { 102 if pattern == "*" { 103 return true 104 } 105 } 106 for _, builtin := range hl.builtins { 107 switch builtin { 108 case MatchBuiltinExternal: 109 if ip.IsGlobalUnicast() && !ip.IsPrivate() { 110 return true 111 } 112 case MatchBuiltinPrivate: 113 if ip.IsPrivate() { 114 return true 115 } 116 case MatchBuiltinLoopback: 117 if ip.IsLoopback() { 118 return true 119 } 120 } 121 } 122 for _, ipNet := range hl.ipNets { 123 if ipNet.Contains(ip) { 124 return true 125 } 126 } 127 return false 128 } 129 130 // MatchHostName checks if the host matches an allow/deny(block) list 131 func (hl *HostMatchList) MatchHostName(host string) bool { 132 if hl == nil { 133 return false 134 } 135 136 hostname, _, err := net.SplitHostPort(host) 137 if err != nil { 138 hostname = host 139 } 140 if hl.checkPattern(hostname) { 141 return true 142 } 143 if ip := net.ParseIP(hostname); ip != nil { 144 return hl.checkIP(ip) 145 } 146 return false 147 } 148 149 // MatchIPAddr checks if the IP matches an allow/deny(block) list, it's safe to pass `nil` to `ip` 150 func (hl *HostMatchList) MatchIPAddr(ip net.IP) bool { 151 if hl == nil { 152 return false 153 } 154 host := ip.String() // nil-safe, we will get "<nil>" if ip is nil 155 return hl.checkPattern(host) || hl.checkIP(ip) 156 } 157 158 // MatchHostOrIP checks if the host or IP matches an allow/deny(block) list 159 func (hl *HostMatchList) MatchHostOrIP(host string, ip net.IP) bool { 160 return hl.MatchHostName(host) || hl.MatchIPAddr(ip) 161 }