| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 | // package for logicing client and server codepackage logicimport (	"crypto/rand"	"encoding/base32"	"encoding/base64"	"encoding/json"	"fmt"	"log/slog"	"net"	"net/http"	"os"	"reflect"	"strings"	"time"	"unicode"	"github.com/blang/semver"	"github.com/c-robinson/iplib"	"github.com/gravitl/netmaker/database"	"github.com/gravitl/netmaker/logger")// IsBase64 - checks if a string is in base64 format// This is used to validate public keys (make sure they're base64 encoded like all public keys should be).func IsBase64(s string) bool {	_, err := base64.StdEncoding.DecodeString(s)	return err == nil}// CheckEndpoint - checks if an endpoint is validfunc CheckEndpoint(endpoint string) bool {	endpointarr := strings.Split(endpoint, ":")	return len(endpointarr) == 2}// FileExists - checks if local file existsfunc FileExists(f string) bool {	info, err := os.Stat(f)	if os.IsNotExist(err) {		return false	}	return !info.IsDir()}// IsAddressInCIDR - util to see if an address is in a cidr or notfunc IsAddressInCIDR(address net.IP, cidr string) bool {	var _, currentCIDR, cidrErr = net.ParseCIDR(cidr)	if cidrErr != nil {		return false	}	return currentCIDR.Contains(address)}// SetNetworkNodesLastModified - sets the network nodes last modifiedfunc SetNetworkNodesLastModified(networkName string) error {	timestamp := time.Now().Unix()	network, err := GetParentNetwork(networkName)	if err != nil {		return err	}	network.NodesLastModified = timestamp	data, err := json.Marshal(&network)	if err != nil {		return err	}	err = database.Insert(networkName, string(data), database.NETWORKS_TABLE_NAME)	if err != nil {		return err	}	return nil}// RandomString - returns a random string in a charsetfunc RandomString(length int) string {	randombytes := make([]byte, length)	_, err := rand.Read(randombytes)	if err != nil {		logger.Log(0, "random string", err.Error())		return ""	}	return base32.StdEncoding.EncodeToString(randombytes)[:length]}// StringSliceContains - sees if a string slice contains a string elementfunc StringSliceContains(slice []string, item string) bool {	for _, s := range slice {		if s == item {			return true		}	}	return false}func SetVerbosity(logLevel int) {	var level slog.Level	switch logLevel {	case 0:		level = slog.LevelInfo	case 1:		level = slog.LevelError	case 2:		level = slog.LevelWarn	case 3:		level = slog.LevelDebug	default:		level = slog.LevelInfo	}	// Create the logger with the chosen level	handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{		Level: level,	})	logger := slog.New(handler)	slog.SetDefault(logger)}// NormalizeCIDR - returns the first address of CIDRfunc NormalizeCIDR(address string) (string, error) {	ip, IPNet, err := net.ParseCIDR(address)	if err != nil {		return "", err	}	if ip.To4() == nil {		net6 := iplib.Net6FromStr(IPNet.String())		IPNet.IP = net6.FirstAddress()	} else {		net4 := iplib.Net4FromStr(IPNet.String())		IPNet.IP = net4.NetworkAddress()	}	return IPNet.String(), nil}// StringDifference - returns the elements in `a` that aren't in `b`.func StringDifference(a, b []string) []string {	mb := make(map[string]struct{}, len(b))	for _, x := range b {		mb[x] = struct{}{}	}	var diff []string	for _, x := range a {		if _, found := mb[x]; !found {			diff = append(diff, x)		}	}	return diff}// CheckIfFileExists - checks if file exists or not in the given pathfunc CheckIfFileExists(filePath string) bool {	if _, err := os.Stat(filePath); os.IsNotExist(err) {		return false	}	return true}// RemoveStringSlice - removes an element at given index i// from a given string slicefunc RemoveStringSlice(slice []string, i int) []string {	return append(slice[:i], slice[i+1:]...)}// IsSlicesEqual tells whether a and b contain the same elements.// A nil argument is equivalent to an empty slice.func IsSlicesEqual(a, b []string) bool {	if len(a) != len(b) {		return false	}	for i, v := range a {		if v != b[i] {			return false		}	}	return true}// VersionLessThan checks if v1 < v2 semantically// dev is the latest versionfunc VersionLessThan(v1, v2 string) (bool, error) {	if v1 == "dev" {		return false, nil	}	if v2 == "dev" {		return true, nil	}	semVer1 := strings.TrimFunc(v1, func(r rune) bool {		return !unicode.IsNumber(r)	})	semVer2 := strings.TrimFunc(v2, func(r rune) bool {		return !unicode.IsNumber(r)	})	sv1, err := semver.Parse(semVer1)	if err != nil {		return false, fmt.Errorf("failed to parse semver1 (%s): %w", semVer1, err)	}	sv2, err := semver.Parse(semVer2)	if err != nil {		return false, fmt.Errorf("failed to parse semver2 (%s): %w", semVer2, err)	}	return sv1.LT(sv2), nil}// Compare any two maps with any key and value typesfunc CompareMaps[K comparable, V any](a, b map[K]V) bool {	if len(a) != len(b) {		return false	}	for key, valA := range a {		valB, ok := b[key]		if !ok {			return false		}		if !reflect.DeepEqual(valA, valB) {			return false		}	}	return true}func GetClientIP(r *http.Request) string {	// Trust X-Forwarded-For first	if xff := r.Header.Get("X-Forwarded-For"); xff != "" {		parts := strings.Split(xff, ",")		return strings.TrimSpace(parts[0])	}	if xrip := r.Header.Get("X-Real-IP"); xrip != "" {		return xrip	}	ip, _, err := net.SplitHostPort(r.RemoteAddr)	if err != nil {		return r.RemoteAddr	}	return ip}
 |