| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- package service
- import (
- "net/netip"
- "strconv"
- "strings"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- "github.com/mhsanaei/3x-ui/v3/internal/util/common"
- wgutil "github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
- )
- const defaultWireguardBase = "10.0.0.0/24"
- func keepAliveStr(seconds int) string {
- if seconds <= 0 {
- return ""
- }
- return strconv.Itoa(seconds)
- }
- func wireguardHostAddr(s string) netip.Addr {
- s = strings.TrimSpace(s)
- if s == "" {
- return netip.Addr{}
- }
- if p, err := netip.ParsePrefix(s); err == nil {
- return p.Addr()
- }
- if a, err := netip.ParseAddr(s); err == nil {
- return a
- }
- return netip.Addr{}
- }
- // allocateWireguardAddress returns the first free /32 host address in base that
- // is not already present in used. The server holds the first host (.1), so
- // allocation starts at the second host (.2).
- func allocateWireguardAddress(used []string, base string) (string, error) {
- if base == "" {
- base = defaultWireguardBase
- }
- prefix, err := netip.ParsePrefix(base)
- if err != nil {
- return "", err
- }
- taken := make(map[netip.Addr]struct{}, len(used))
- for _, u := range used {
- if a := wireguardHostAddr(u); a.IsValid() {
- taken[a] = struct{}{}
- }
- }
- addr := prefix.Masked().Addr().Next().Next()
- for prefix.Contains(addr) {
- if _, ok := taken[addr]; !ok {
- return addr.String() + "/32", nil
- }
- addr = addr.Next()
- }
- return "", common.NewError("wireguard: no free address available in", base)
- }
- // defaultWireguardClients fills in blank WireGuard credentials for newly added
- // clients: a generated keypair when none was provided, a derived public key when
- // only a private key was given, and a unique tunnel address allocated from the
- // inbound's subnet. It mutates both the typed clients and the parallel raw client
- // maps that get persisted into the inbound settings. Existing values are never
- // overwritten, so editing a client never rotates its keys.
- func defaultWireguardClients(existing, clients []model.Client, interfaceClients []any) error {
- used := make([]string, 0)
- for i := range existing {
- used = append(used, existing[i].AllowedIPs...)
- }
- for i := range clients {
- c := &clients[i]
- if c.PrivateKey == "" && c.PublicKey == "" {
- priv, pub, err := wgutil.GenerateWireguardKeypair()
- if err != nil {
- return err
- }
- c.PrivateKey = priv
- c.PublicKey = pub
- } else if c.PublicKey == "" && c.PrivateKey != "" {
- pub, err := wgutil.PublicKeyFromPrivate(c.PrivateKey)
- if err != nil {
- return err
- }
- c.PublicKey = pub
- }
- if len(c.AllowedIPs) == 0 {
- addr, err := allocateWireguardAddress(used, defaultWireguardBase)
- if err != nil {
- return err
- }
- c.AllowedIPs = []string{addr}
- }
- used = append(used, c.AllowedIPs...)
- if i < len(interfaceClients) {
- if m, ok := interfaceClients[i].(map[string]any); ok {
- m["privateKey"] = c.PrivateKey
- m["publicKey"] = c.PublicKey
- m["allowedIPs"] = c.AllowedIPs
- if c.PreSharedKey != "" {
- m["preSharedKey"] = c.PreSharedKey
- }
- if c.KeepAlive > 0 {
- m["keepAlive"] = c.KeepAlive
- }
- interfaceClients[i] = m
- }
- }
- }
- return nil
- }
|