You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
3.8 KiB
215 lines
3.8 KiB
package wireguard |
|
|
|
import ( |
|
"encoding/base64" |
|
"errors" |
|
"fmt" |
|
"io" |
|
"net/netip" |
|
"strconv" |
|
"strings" |
|
|
|
"darvaza.org/core" |
|
"gopkg.in/gcfg.v1" |
|
) |
|
|
|
// Config represents a wgN.conf file |
|
type Config struct { |
|
Interface InterfaceConfig |
|
Peer []PeerConfig |
|
} |
|
|
|
// GetAddress is a shortcut to the interface's address |
|
func (f *Config) GetAddress() netip.Addr { |
|
return f.Interface.Address |
|
} |
|
|
|
// Peers tells how many peers are described |
|
func (f *Config) Peers() int { |
|
return len(f.Peer) |
|
} |
|
|
|
// InterfaceConfig represents the [Interface] section |
|
type InterfaceConfig struct { |
|
Address netip.Addr |
|
PrivateKey []byte |
|
ListenPort uint16 |
|
} |
|
|
|
// PeerConfig represents a [Peer] section |
|
type PeerConfig struct { |
|
PublicKey []byte |
|
Endpoint EndpointAddress |
|
AllowedIPs []netip.Prefix |
|
} |
|
|
|
// EndpointAddress is a host:port pair to reach the Peer |
|
type EndpointAddress struct { |
|
Host string |
|
Port uint16 |
|
} |
|
|
|
func (ep EndpointAddress) String() string { |
|
switch { |
|
case ep.Host == "": |
|
return "" |
|
case ep.Port == 0: |
|
return ep.Host |
|
case !strings.ContainsRune(ep.Host, ':'): |
|
return fmt.Sprintf("%s:%v", ep.Host, ep.Port) |
|
default: |
|
return fmt.Sprintf("[%s]:%v", ep.Host, ep.Port) |
|
} |
|
} |
|
|
|
// FromString sets the EndpointAddress from a given "[host]:port" |
|
func (ep *EndpointAddress) FromString(s string) error { |
|
host, port, err := core.SplitHostPort(s) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
ep.Host = host |
|
|
|
switch { |
|
case port != "": |
|
n, _ := strconv.ParseUint(port, 10, 16) |
|
ep.Port = uint16(n) |
|
default: |
|
ep.Port = 0 |
|
} |
|
|
|
return nil |
|
} |
|
|
|
type intermediateConfig struct { |
|
Interface interfaceConfig |
|
Peer peersConfig |
|
} |
|
|
|
func (v *intermediateConfig) Export() (*Config, error) { |
|
var out Config |
|
var err error |
|
|
|
// Interface |
|
out.Interface, err = v.Interface.Export() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
// Peers |
|
peers, ok := v.PeersCount() |
|
if !ok { |
|
return nil, errors.New("inconsistent Peer data") |
|
} |
|
|
|
for i := 0; i < peers; i++ { |
|
p, err := v.ExportPeer(i) |
|
if err != nil { |
|
err = core.Wrapf(err, "Peer[%v]:", i) |
|
return nil, err |
|
} |
|
|
|
out.Peer = append(out.Peer, p) |
|
} |
|
|
|
return &out, nil |
|
} |
|
|
|
type interfaceConfig struct { |
|
Address netip.Addr |
|
PrivateKey string |
|
ListenPort uint16 |
|
} |
|
|
|
func (p interfaceConfig) Export() (InterfaceConfig, error) { |
|
out := InterfaceConfig{ |
|
Address: p.Address, |
|
ListenPort: p.ListenPort, |
|
} |
|
|
|
b, err := base64.StdEncoding.DecodeString(p.PrivateKey) |
|
if err != nil { |
|
err = core.Wrap(err, "PrivateKey") |
|
return InterfaceConfig{}, err |
|
} |
|
|
|
out.PrivateKey = b |
|
|
|
return out, nil |
|
} |
|
|
|
type peersConfig struct { |
|
PublicKey []string |
|
Endpoint []string |
|
AllowedIPs []string |
|
} |
|
|
|
func (v *intermediateConfig) ExportPeer(i int) (PeerConfig, error) { |
|
var out PeerConfig |
|
|
|
// Endpoint |
|
s := v.Peer.Endpoint[i] |
|
err := out.Endpoint.FromString(s) |
|
if err != nil { |
|
err = core.Wrap(err, "Endpoint") |
|
return out, err |
|
} |
|
|
|
// PublicKey |
|
s = v.Peer.PublicKey[i] |
|
out.PublicKey, err = base64.StdEncoding.DecodeString(s) |
|
if err != nil { |
|
err = core.Wrap(err, "PublicKey") |
|
return out, err |
|
} |
|
|
|
// AllowedIPs |
|
s = v.Peer.AllowedIPs[i] |
|
out.AllowedIPs, err = parseAllowedIPs(s) |
|
if err != nil { |
|
err = core.Wrap(err, "AllowedIPs") |
|
return out, err |
|
} |
|
|
|
return out, nil |
|
} |
|
|
|
func parseAllowedIPs(data string) ([]netip.Prefix, error) { |
|
var out []netip.Prefix |
|
|
|
for _, s := range strings.Split(data, ",") { |
|
s = strings.TrimSpace(s) |
|
p, err := netip.ParsePrefix(s) |
|
if err != nil { |
|
return out, err |
|
} |
|
|
|
out = append(out, p) |
|
} |
|
|
|
return out, nil |
|
} |
|
|
|
func (v *intermediateConfig) PeersCount() (int, bool) { |
|
c0 := len(v.Peer.Endpoint) |
|
c1 := len(v.Peer.PublicKey) |
|
c2 := len(v.Peer.AllowedIPs) |
|
|
|
if c0 != c1 || c1 != c2 { |
|
return 0, false |
|
} |
|
|
|
return c0, true |
|
} |
|
|
|
// NewConfigFromReader parses a wgN.conf file |
|
func NewConfigFromReader(r io.Reader) (*Config, error) { |
|
temp := &intermediateConfig{} |
|
|
|
if err := gcfg.ReadInto(temp, r); err != nil { |
|
return nil, err |
|
} |
|
|
|
return temp.Export() |
|
}
|
|
|