util.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // package for logicing client and server code
  2. package logic
  3. import (
  4. "crypto/rand"
  5. "encoding/base32"
  6. "encoding/base64"
  7. "encoding/json"
  8. "fmt"
  9. "log/slog"
  10. "net"
  11. "net/http"
  12. "os"
  13. "reflect"
  14. "regexp"
  15. "strings"
  16. "time"
  17. "unicode"
  18. "github.com/blang/semver"
  19. "github.com/c-robinson/iplib"
  20. "github.com/gravitl/netmaker/database"
  21. "github.com/gravitl/netmaker/logger"
  22. "github.com/gravitl/netmaker/models"
  23. )
  24. // IsBase64 - checks if a string is in base64 format
  25. // This is used to validate public keys (make sure they're base64 encoded like all public keys should be).
  26. func IsBase64(s string) bool {
  27. _, err := base64.StdEncoding.DecodeString(s)
  28. return err == nil
  29. }
  30. // CheckEndpoint - checks if an endpoint is valid
  31. func CheckEndpoint(endpoint string) bool {
  32. endpointarr := strings.Split(endpoint, ":")
  33. return len(endpointarr) == 2
  34. }
  35. // FileExists - checks if local file exists
  36. func FileExists(f string) bool {
  37. info, err := os.Stat(f)
  38. if os.IsNotExist(err) {
  39. return false
  40. }
  41. return !info.IsDir()
  42. }
  43. // IsAddressInCIDR - util to see if an address is in a cidr or not
  44. func IsAddressInCIDR(address net.IP, cidr string) bool {
  45. var _, currentCIDR, cidrErr = net.ParseCIDR(cidr)
  46. if cidrErr != nil {
  47. return false
  48. }
  49. return currentCIDR.Contains(address)
  50. }
  51. // SetNetworkNodesLastModified - sets the network nodes last modified
  52. func SetNetworkNodesLastModified(networkName string) error {
  53. timestamp := time.Now().Unix()
  54. network, err := GetParentNetwork(networkName)
  55. if err != nil {
  56. return err
  57. }
  58. network.NodesLastModified = timestamp
  59. data, err := json.Marshal(&network)
  60. if err != nil {
  61. return err
  62. }
  63. err = database.Insert(networkName, string(data), database.NETWORKS_TABLE_NAME)
  64. if err != nil {
  65. return err
  66. }
  67. return nil
  68. }
  69. // RandomString - returns a random string in a charset
  70. func RandomString(length int) string {
  71. randombytes := make([]byte, length)
  72. _, err := rand.Read(randombytes)
  73. if err != nil {
  74. logger.Log(0, "random string", err.Error())
  75. return ""
  76. }
  77. return base32.StdEncoding.EncodeToString(randombytes)[:length]
  78. }
  79. // StringSliceContains - sees if a string slice contains a string element
  80. func StringSliceContains(slice []string, item string) bool {
  81. for _, s := range slice {
  82. if s == item {
  83. return true
  84. }
  85. }
  86. return false
  87. }
  88. func SetVerbosity(logLevel int) {
  89. var level slog.Level
  90. switch logLevel {
  91. case 0:
  92. level = slog.LevelInfo
  93. case 1:
  94. level = slog.LevelError
  95. case 2:
  96. level = slog.LevelWarn
  97. case 3:
  98. level = slog.LevelDebug
  99. default:
  100. level = slog.LevelInfo
  101. }
  102. // Create the logger with the chosen level
  103. handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
  104. Level: level,
  105. })
  106. logger := slog.New(handler)
  107. slog.SetDefault(logger)
  108. }
  109. // NormalizeCIDR - returns the first address of CIDR
  110. func NormalizeCIDR(address string) (string, error) {
  111. ip, IPNet, err := net.ParseCIDR(address)
  112. if err != nil {
  113. return "", err
  114. }
  115. if ip.To4() == nil {
  116. net6 := iplib.Net6FromStr(IPNet.String())
  117. IPNet.IP = net6.FirstAddress()
  118. } else {
  119. net4 := iplib.Net4FromStr(IPNet.String())
  120. IPNet.IP = net4.NetworkAddress()
  121. }
  122. return IPNet.String(), nil
  123. }
  124. // StringDifference - returns the elements in `a` that aren't in `b`.
  125. func StringDifference(a, b []string) []string {
  126. mb := make(map[string]struct{}, len(b))
  127. for _, x := range b {
  128. mb[x] = struct{}{}
  129. }
  130. var diff []string
  131. for _, x := range a {
  132. if _, found := mb[x]; !found {
  133. diff = append(diff, x)
  134. }
  135. }
  136. return diff
  137. }
  138. // CheckIfFileExists - checks if file exists or not in the given path
  139. func CheckIfFileExists(filePath string) bool {
  140. if _, err := os.Stat(filePath); os.IsNotExist(err) {
  141. return false
  142. }
  143. return true
  144. }
  145. // RemoveStringSlice - removes an element at given index i
  146. // from a given string slice
  147. func RemoveStringSlice(slice []string, i int) []string {
  148. return append(slice[:i], slice[i+1:]...)
  149. }
  150. // IsSlicesEqual tells whether a and b contain the same elements.
  151. // A nil argument is equivalent to an empty slice.
  152. func IsSlicesEqual(a, b []string) bool {
  153. if len(a) != len(b) {
  154. return false
  155. }
  156. for i, v := range a {
  157. if v != b[i] {
  158. return false
  159. }
  160. }
  161. return true
  162. }
  163. // VersionLessThan checks if v1 < v2 semantically
  164. // dev is the latest version
  165. func VersionLessThan(v1, v2 string) (bool, error) {
  166. if v1 == "dev" {
  167. return false, nil
  168. }
  169. if v2 == "dev" {
  170. return true, nil
  171. }
  172. semVer1 := strings.TrimFunc(v1, func(r rune) bool {
  173. return !unicode.IsNumber(r)
  174. })
  175. semVer2 := strings.TrimFunc(v2, func(r rune) bool {
  176. return !unicode.IsNumber(r)
  177. })
  178. sv1, err := semver.Parse(semVer1)
  179. if err != nil {
  180. return false, fmt.Errorf("failed to parse semver1 (%s): %w", semVer1, err)
  181. }
  182. sv2, err := semver.Parse(semVer2)
  183. if err != nil {
  184. return false, fmt.Errorf("failed to parse semver2 (%s): %w", semVer2, err)
  185. }
  186. return sv1.LT(sv2), nil
  187. }
  188. // Compare any two maps with any key and value types
  189. func CompareMaps[K comparable, V any](a, b map[K]V) bool {
  190. if len(a) != len(b) {
  191. return false
  192. }
  193. for key, valA := range a {
  194. valB, ok := b[key]
  195. if !ok {
  196. return false
  197. }
  198. if !reflect.DeepEqual(valA, valB) {
  199. return false
  200. }
  201. }
  202. return true
  203. }
  204. func UniqueStrings(input []string) []string {
  205. seen := make(map[string]struct{})
  206. var result []string
  207. for _, val := range input {
  208. if _, ok := seen[val]; !ok {
  209. seen[val] = struct{}{}
  210. result = append(result, val)
  211. }
  212. }
  213. return result
  214. }
  215. func GetClientIP(r *http.Request) string {
  216. // Trust X-Forwarded-For first
  217. if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
  218. parts := strings.Split(xff, ",")
  219. return strings.TrimSpace(parts[0])
  220. }
  221. if xrip := r.Header.Get("X-Real-IP"); xrip != "" {
  222. return xrip
  223. }
  224. ip, _, err := net.SplitHostPort(r.RemoteAddr)
  225. if err != nil {
  226. return r.RemoteAddr
  227. }
  228. return ip
  229. }
  230. // CompareIfaceSlices compares two slices of Iface for deep equality (order-sensitive)
  231. func CompareIfaceSlices(a, b []models.Iface) bool {
  232. if len(a) != len(b) {
  233. return false
  234. }
  235. for i := range a {
  236. if !compareIface(a[i], b[i]) {
  237. return false
  238. }
  239. }
  240. return true
  241. }
  242. func compareIface(a, b models.Iface) bool {
  243. return a.Name == b.Name &&
  244. a.Address.IP.Equal(b.Address.IP) &&
  245. a.Address.Mask.String() == b.Address.Mask.String() &&
  246. a.AddressString == b.AddressString
  247. }
  248. // IsFQDN checks if the given string is a valid Fully Qualified Domain Name (FQDN)
  249. func IsFQDN(domain string) bool {
  250. // Basic check to ensure the domain is not empty and has at least one dot (.)
  251. if domain == "" || !strings.Contains(domain, ".") {
  252. return false
  253. }
  254. // Regular expression for validating FQDN (basic check for valid characters and structure)
  255. fqdnRegex := `^(?i)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`
  256. re := regexp.MustCompile(fqdnRegex)
  257. return re.MatchString(domain)
  258. }