diff --git a/configreader.go b/configreader.go new file mode 100644 index 0000000..410f4ed --- /dev/null +++ b/configreader.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "io/fs" + "net" + "path/filepath" + "strings" + + "gopkg.in/ini.v1" +) + +func parseUsedIPsFromConfigs(dir string) ([]net.IP, error) { + var usedIPs []net.IP + + err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() || !strings.HasSuffix(d.Name(), ".conf") { + return nil + } + + cfg, err := ini.Load(path) + if err != nil { + return fmt.Errorf("failed to parse config %s: %w", path, err) + } + + // Check [Interface] section + if iface := cfg.Section("Interface"); iface != nil { + if addr := iface.Key("Address").String(); addr != "" { + ip := strings.Split(addr, "/")[0] + usedIPs = append(usedIPs, net.ParseIP(ip)) + } + } + + return nil + }) + + if err != nil { + return nil, err + } + + return usedIPs, nil +} diff --git a/go.mod b/go.mod index fc7dd99..2540c39 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,9 @@ require ( ) require ( + github.com/joho/godotenv v1.5.1 // indirect github.com/pquerna/cachecontrol v0.2.0 // indirect golang.org/x/crypto v0.37.0 // indirect gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index 668ca51..37d181c 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k= @@ -18,5 +20,7 @@ golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs= gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ipaddresses.go b/ipaddresses.go new file mode 100644 index 0000000..b68cc17 --- /dev/null +++ b/ipaddresses.go @@ -0,0 +1,100 @@ +package main + +import ( + "fmt" + "log" + "net" + "os" +) + +func getInterfaceIPAndMask(interfaceName string) (ip net.IP, mask net.IPMask, err error) { + iface, err := net.InterfaceByName(interfaceName) + if err != nil { + return nil, nil, fmt.Errorf("failed to get interface %s: %w", interfaceName, err) + } + + addrs, err := iface.Addrs() + if err != nil { + return nil, nil, fmt.Errorf("failed to get addresses for %s: %w", interfaceName, err) + } + + for _, addr := range addrs { + ipNet, ok := addr.(*net.IPNet) + if !ok || ipNet.IP.IsLoopback() || ipNet.IP.To4() == nil { + continue + } + + return ipNet.IP, ipNet.Mask, nil + } + + return nil, nil, fmt.Errorf("no valid IPv4 address found for interface %s", interfaceName) +} + +func getNextFreeIP() (net.IP, error) { + var wg string = os.Getenv("WIREGUARD_INTERFACE") + ip, mask, err := getInterfaceIPAndMask(wg) + if err != nil { + log.Fatal(err) + } + used, err := parseUsedIPsFromConfigs("./configs") + if err != nil { + fmt.Println("Error:", err) + return nil, fmt.Errorf("Unkown err: %s", err) + } + + for _, ip := range used { + fmt.Println("Used IP:", ip) + } + // used := []net.IP{ + // net.ParseIP("10.200.34.1"), + // net.ParseIP("10.200.34.2"), + // net.ParseIP("10.200.34.3"), + // net.ParseIP("10.200.34.4"), + // net.ParseIP("10.200.34.5"), + // net.ParseIP("10.200.34.6"), + // net.ParseIP("10.200.34.225"), + // net.ParseIP("10.200.34.226"), + // net.ParseIP("10.200.34.227"), + // } + + network := ip.Mask(mask) + ipNet := &net.IPNet{IP: network, Mask: mask} + + // Turn the used list into a map for fast lookup + usedMap := make(map[string]bool) + for _, u := range used { + usedMap[u.String()] = true + } + + // Get network boundaries + start := ipToUint32(network) + 1 + end := ipToUint32(lastIP(ipNet)) - 1 // Skip broadcast + + for i := start; i <= end; i++ { + candidate := uint32ToIP(i) + if !usedMap[candidate.String()] { + return candidate, nil + } + } + + return nil, fmt.Errorf("no free IPs in subnet %s", ipNet.String()) +} + +func ipToUint32(ip net.IP) uint32 { + ip = ip.To4() + return uint32(ip[0])<<24 | uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3]) +} + +func uint32ToIP(n uint32) net.IP { + return net.IPv4(byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) +} + +func lastIP(ipNet *net.IPNet) net.IP { + ip := ipNet.IP.To4() + mask := ipNet.Mask + broadcast := make(net.IP, len(ip)) + for i := range ip { + broadcast[i] = ip[i] | ^mask[i] + } + return broadcast +} diff --git a/main.go b/main.go index 6396ddb..679f090 100644 --- a/main.go +++ b/main.go @@ -5,12 +5,12 @@ import ( "fmt" "log" "net/http" - "strings" "os" + "strings" + "github.com/coreos/go-oidc" "golang.org/x/oauth2" "golang.org/x/oauth2/microsoft" - "github.com/coreos/go-oidc" ) var ( @@ -26,10 +26,16 @@ var ( func main() { + nextIP, err := getNextFreeIP() + if err != nil { + fmt.Println("Error:", err) + } else { + fmt.Println("Next available IP:", nextIP) + } + ctx := context.Background() issuer := fmt.Sprintf("https://login.microsoftonline.com/%s/v2.0", tenantID) - var err error provider, err = oidc.NewProvider(ctx, issuer) if err != nil { log.Fatalf("failed to get provider: %v", err) @@ -50,6 +56,7 @@ func main() { log.Println("Server started at http://localhost:5000") log.Fatal(http.ListenAndServe(":5000", nil)) + } func handleIndex(w http.ResponseWriter, r *http.Request) { @@ -77,7 +84,7 @@ func handleIndex(w http.ResponseWriter, r *http.Request) { } var claims struct { - Email string `json:"email"` + Email string `json:"email"` PreferredUsername string `json:"preferred_username"` Name string `json:"name"` }