|
|
|
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()
|
|
|
|
}
|