4 changed files with 210 additions and 1 deletions
			
			
		| @ -0,0 +1,201 @@ | ||||
| package wireguard | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"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 { | ||||
| 	Name string | ||||
| 	Port uint16 | ||||
| } | ||||
| 
 | ||||
| // 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.Name = 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() | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue