golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/conf/name.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package conf 7 8 import ( 9 "errors" 10 "regexp" 11 "strconv" 12 "strings" 13 ) 14 15 var reservedNames = []string{ 16 "CON", "PRN", "AUX", "NUL", 17 "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", 18 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", 19 } 20 21 const ( 22 serviceNameForbidden = "$" 23 netshellDllForbidden = "\\/:*?\"<>|\t" 24 specialChars = "/\\<>:\"|?*\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x00" 25 ) 26 27 var allowedNameFormat = regexp.MustCompile("^[a-zA-Z0-9_=+.-]{1,32}$") 28 29 func isReserved(name string) bool { 30 if len(name) == 0 { 31 return false 32 } 33 for _, reserved := range reservedNames { 34 if strings.EqualFold(name, reserved) { 35 return true 36 } 37 for i := len(name) - 1; i >= 0; i-- { 38 if name[i] == '.' { 39 if strings.EqualFold(name[:i], reserved) { 40 return true 41 } 42 break 43 } 44 } 45 } 46 return false 47 } 48 49 func hasSpecialChars(name string) bool { 50 return strings.ContainsAny(name, specialChars) || strings.ContainsAny(name, netshellDllForbidden) || strings.ContainsAny(name, serviceNameForbidden) 51 } 52 53 func TunnelNameIsValid(name string) bool { 54 // Aside from our own restrictions, let's impose the Windows restrictions first 55 if isReserved(name) || hasSpecialChars(name) { 56 return false 57 } 58 return allowedNameFormat.MatchString(name) 59 } 60 61 type naturalSortToken struct { 62 maybeString string 63 maybeNumber int 64 } 65 66 type naturalSortString struct { 67 originalString string 68 tokens []naturalSortToken 69 } 70 71 var naturalSortDigitFinder = regexp.MustCompile(`\d+|\D+`) 72 73 func newNaturalSortString(s string) (t naturalSortString) { 74 t.originalString = s 75 s = strings.ToLower(strings.Join(strings.Fields(s), " ")) 76 x := naturalSortDigitFinder.FindAllString(s, -1) 77 t.tokens = make([]naturalSortToken, len(x)) 78 for i, s := range x { 79 if n, err := strconv.Atoi(s); err == nil { 80 t.tokens[i].maybeNumber = n 81 } else { 82 t.tokens[i].maybeString = s 83 } 84 } 85 return 86 } 87 88 func (f1 naturalSortToken) Cmp(f2 naturalSortToken) int { 89 if len(f1.maybeString) == 0 { 90 if len(f2.maybeString) > 0 || f1.maybeNumber < f2.maybeNumber { 91 return -1 92 } else if f1.maybeNumber > f2.maybeNumber { 93 return 1 94 } 95 } else if len(f2.maybeString) == 0 || f1.maybeString > f2.maybeString { 96 return 1 97 } else if f1.maybeString < f2.maybeString { 98 return -1 99 } 100 return 0 101 } 102 103 func TunnelNameIsLess(a, b string) bool { 104 if a == b { 105 return false 106 } 107 na, nb := newNaturalSortString(a), newNaturalSortString(b) 108 for i, t := range nb.tokens { 109 if i == len(na.tokens) { 110 return true 111 } 112 switch na.tokens[i].Cmp(t) { 113 case -1: 114 return true 115 case 1: 116 return false 117 } 118 } 119 return false 120 } 121 122 func ServiceNameOfTunnel(tunnelName string) (string, error) { 123 if !TunnelNameIsValid(tunnelName) { 124 return "", errors.New("Tunnel name is not valid") 125 } 126 return "WireGuardTunnel$" + tunnelName, nil 127 }