mirror of
https://git.zx2c4.com/wireguard-windows
synced 2024-11-10 16:59:18 +00:00
6ed37f30f5
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
401 lines
9.9 KiB
Go
401 lines
9.9 KiB
Go
/* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
|
|
*/
|
|
|
|
package conf
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"net/netip"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/sys/windows"
|
|
"golang.org/x/text/encoding/unicode"
|
|
|
|
"golang.zx2c4.com/wireguard/windows/driver"
|
|
"golang.zx2c4.com/wireguard/windows/l18n"
|
|
)
|
|
|
|
type ParseError struct {
|
|
why string
|
|
offender string
|
|
}
|
|
|
|
func (e *ParseError) Error() string {
|
|
return l18n.Sprintf("%s: %q", e.why, e.offender)
|
|
}
|
|
|
|
func parseIPCidr(s string) (netip.Prefix, error) {
|
|
ipcidr, err := netip.ParsePrefix(s)
|
|
if err == nil {
|
|
return ipcidr, nil
|
|
}
|
|
addr, err := netip.ParseAddr(s)
|
|
if err != nil {
|
|
return netip.Prefix{}, &ParseError{l18n.Sprintf("Invalid IP address: "), s}
|
|
}
|
|
return netip.PrefixFrom(addr, addr.BitLen()), nil
|
|
}
|
|
|
|
func parseEndpoint(s string) (*Endpoint, error) {
|
|
i := strings.LastIndexByte(s, ':')
|
|
if i < 0 {
|
|
return nil, &ParseError{l18n.Sprintf("Missing port from endpoint"), s}
|
|
}
|
|
host, portStr := s[:i], s[i+1:]
|
|
if len(host) < 1 {
|
|
return nil, &ParseError{l18n.Sprintf("Invalid endpoint host"), host}
|
|
}
|
|
port, err := parsePort(portStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hostColon := strings.IndexByte(host, ':')
|
|
if host[0] == '[' || host[len(host)-1] == ']' || hostColon > 0 {
|
|
err := &ParseError{l18n.Sprintf("Brackets must contain an IPv6 address"), host}
|
|
if len(host) > 3 && host[0] == '[' && host[len(host)-1] == ']' && hostColon > 0 {
|
|
end := len(host) - 1
|
|
if i := strings.LastIndexByte(host, '%'); i > 1 {
|
|
end = i
|
|
}
|
|
maybeV6, err2 := netip.ParseAddr(host[1:end])
|
|
if err2 != nil || !maybeV6.Is6() {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return nil, err
|
|
}
|
|
host = host[1 : len(host)-1]
|
|
}
|
|
return &Endpoint{host, port}, nil
|
|
}
|
|
|
|
func parseMTU(s string) (uint16, error) {
|
|
m, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if m < 576 || m > 65535 {
|
|
return 0, &ParseError{l18n.Sprintf("Invalid MTU"), s}
|
|
}
|
|
return uint16(m), nil
|
|
}
|
|
|
|
func parsePort(s string) (uint16, error) {
|
|
m, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if m < 0 || m > 65535 {
|
|
return 0, &ParseError{l18n.Sprintf("Invalid port"), s}
|
|
}
|
|
return uint16(m), nil
|
|
}
|
|
|
|
func parsePersistentKeepalive(s string) (uint16, error) {
|
|
if s == "off" {
|
|
return 0, nil
|
|
}
|
|
m, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if m < 0 || m > 65535 {
|
|
return 0, &ParseError{l18n.Sprintf("Invalid persistent keepalive"), s}
|
|
}
|
|
return uint16(m), nil
|
|
}
|
|
|
|
func parseTableOff(s string) (bool, error) {
|
|
if s == "off" {
|
|
return true, nil
|
|
} else if s == "auto" || s == "main" {
|
|
return false, nil
|
|
}
|
|
_, err := strconv.ParseUint(s, 10, 32)
|
|
return false, err
|
|
}
|
|
|
|
func parseKeyBase64(s string) (*Key, error) {
|
|
k, err := base64.StdEncoding.DecodeString(s)
|
|
if err != nil {
|
|
return nil, &ParseError{l18n.Sprintf("Invalid key: %v", err), s}
|
|
}
|
|
if len(k) != KeyLength {
|
|
return nil, &ParseError{l18n.Sprintf("Keys must decode to exactly 32 bytes"), s}
|
|
}
|
|
var key Key
|
|
copy(key[:], k)
|
|
return &key, nil
|
|
}
|
|
|
|
func splitList(s string) ([]string, error) {
|
|
var out []string
|
|
for _, split := range strings.Split(s, ",") {
|
|
trim := strings.TrimSpace(split)
|
|
if len(trim) == 0 {
|
|
return nil, &ParseError{l18n.Sprintf("Two commas in a row"), s}
|
|
}
|
|
out = append(out, trim)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
type parserState int
|
|
|
|
const (
|
|
inInterfaceSection parserState = iota
|
|
inPeerSection
|
|
notInASection
|
|
)
|
|
|
|
func (c *Config) maybeAddPeer(p *Peer) {
|
|
if p != nil {
|
|
c.Peers = append(c.Peers, *p)
|
|
}
|
|
}
|
|
|
|
func FromWgQuick(s, name string) (*Config, error) {
|
|
if !TunnelNameIsValid(name) {
|
|
return nil, &ParseError{l18n.Sprintf("Tunnel name is not valid"), name}
|
|
}
|
|
lines := strings.Split(s, "\n")
|
|
parserState := notInASection
|
|
conf := Config{Name: name}
|
|
sawPrivateKey := false
|
|
var peer *Peer
|
|
for _, line := range lines {
|
|
line, _, _ = strings.Cut(line, "#")
|
|
line = strings.TrimSpace(line)
|
|
lineLower := strings.ToLower(line)
|
|
if len(line) == 0 {
|
|
continue
|
|
}
|
|
if lineLower == "[interface]" {
|
|
conf.maybeAddPeer(peer)
|
|
parserState = inInterfaceSection
|
|
continue
|
|
}
|
|
if lineLower == "[peer]" {
|
|
conf.maybeAddPeer(peer)
|
|
peer = &Peer{}
|
|
parserState = inPeerSection
|
|
continue
|
|
}
|
|
if parserState == notInASection {
|
|
return nil, &ParseError{l18n.Sprintf("Line must occur in a section"), line}
|
|
}
|
|
equals := strings.IndexByte(line, '=')
|
|
if equals < 0 {
|
|
return nil, &ParseError{l18n.Sprintf("Config key is missing an equals separator"), line}
|
|
}
|
|
key, val := strings.TrimSpace(lineLower[:equals]), strings.TrimSpace(line[equals+1:])
|
|
if len(val) == 0 {
|
|
return nil, &ParseError{l18n.Sprintf("Key must have a value"), line}
|
|
}
|
|
if parserState == inInterfaceSection {
|
|
switch key {
|
|
case "privatekey":
|
|
k, err := parseKeyBase64(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conf.Interface.PrivateKey = *k
|
|
sawPrivateKey = true
|
|
case "listenport":
|
|
p, err := parsePort(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conf.Interface.ListenPort = p
|
|
case "mtu":
|
|
m, err := parseMTU(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conf.Interface.MTU = m
|
|
case "address":
|
|
addresses, err := splitList(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, address := range addresses {
|
|
a, err := parseIPCidr(address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conf.Interface.Addresses = append(conf.Interface.Addresses, a)
|
|
}
|
|
case "dns":
|
|
addresses, err := splitList(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, address := range addresses {
|
|
a, err := netip.ParseAddr(address)
|
|
if err != nil {
|
|
conf.Interface.DNSSearch = append(conf.Interface.DNSSearch, address)
|
|
} else {
|
|
conf.Interface.DNS = append(conf.Interface.DNS, a)
|
|
}
|
|
}
|
|
case "preup":
|
|
conf.Interface.PreUp = val
|
|
case "postup":
|
|
conf.Interface.PostUp = val
|
|
case "predown":
|
|
conf.Interface.PreDown = val
|
|
case "postdown":
|
|
conf.Interface.PostDown = val
|
|
case "table":
|
|
tableOff, err := parseTableOff(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conf.Interface.TableOff = tableOff
|
|
default:
|
|
return nil, &ParseError{l18n.Sprintf("Invalid key for [Interface] section"), key}
|
|
}
|
|
} else if parserState == inPeerSection {
|
|
switch key {
|
|
case "publickey":
|
|
k, err := parseKeyBase64(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
peer.PublicKey = *k
|
|
case "presharedkey":
|
|
k, err := parseKeyBase64(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
peer.PresharedKey = *k
|
|
case "allowedips":
|
|
addresses, err := splitList(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, address := range addresses {
|
|
a, err := parseIPCidr(address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
peer.AllowedIPs = append(peer.AllowedIPs, a)
|
|
}
|
|
case "persistentkeepalive":
|
|
p, err := parsePersistentKeepalive(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
peer.PersistentKeepalive = p
|
|
case "endpoint":
|
|
e, err := parseEndpoint(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
peer.Endpoint = *e
|
|
default:
|
|
return nil, &ParseError{l18n.Sprintf("Invalid key for [Peer] section"), key}
|
|
}
|
|
}
|
|
}
|
|
conf.maybeAddPeer(peer)
|
|
|
|
if !sawPrivateKey {
|
|
return nil, &ParseError{l18n.Sprintf("An interface must have a private key"), l18n.Sprintf("[none specified]")}
|
|
}
|
|
for _, p := range conf.Peers {
|
|
if p.PublicKey.IsZero() {
|
|
return nil, &ParseError{l18n.Sprintf("All peers must have public keys"), l18n.Sprintf("[none specified]")}
|
|
}
|
|
}
|
|
|
|
return &conf, nil
|
|
}
|
|
|
|
func FromWgQuickWithUnknownEncoding(s, name string) (*Config, error) {
|
|
c, firstErr := FromWgQuick(s, name)
|
|
if firstErr == nil {
|
|
return c, nil
|
|
}
|
|
for _, encoding := range unicode.All {
|
|
decoded, err := encoding.NewDecoder().String(s)
|
|
if err == nil {
|
|
c, err := FromWgQuick(decoded, name)
|
|
if err == nil {
|
|
return c, nil
|
|
}
|
|
}
|
|
}
|
|
return nil, firstErr
|
|
}
|
|
|
|
func FromDriverConfiguration(interfaze *driver.Interface, existingConfig *Config) *Config {
|
|
conf := Config{
|
|
Name: existingConfig.Name,
|
|
Interface: Interface{
|
|
Addresses: existingConfig.Interface.Addresses,
|
|
DNS: existingConfig.Interface.DNS,
|
|
DNSSearch: existingConfig.Interface.DNSSearch,
|
|
MTU: existingConfig.Interface.MTU,
|
|
PreUp: existingConfig.Interface.PreUp,
|
|
PostUp: existingConfig.Interface.PostUp,
|
|
PreDown: existingConfig.Interface.PreDown,
|
|
PostDown: existingConfig.Interface.PostDown,
|
|
TableOff: existingConfig.Interface.TableOff,
|
|
},
|
|
}
|
|
if interfaze.Flags&driver.InterfaceHasPrivateKey != 0 {
|
|
conf.Interface.PrivateKey = interfaze.PrivateKey
|
|
}
|
|
if interfaze.Flags&driver.InterfaceHasListenPort != 0 {
|
|
conf.Interface.ListenPort = interfaze.ListenPort
|
|
}
|
|
var p *driver.Peer
|
|
for i := uint32(0); i < interfaze.PeerCount; i++ {
|
|
if p == nil {
|
|
p = interfaze.FirstPeer()
|
|
} else {
|
|
p = p.NextPeer()
|
|
}
|
|
peer := Peer{}
|
|
if p.Flags&driver.PeerHasPublicKey != 0 {
|
|
peer.PublicKey = p.PublicKey
|
|
}
|
|
if p.Flags&driver.PeerHasPresharedKey != 0 {
|
|
peer.PresharedKey = p.PresharedKey
|
|
}
|
|
if p.Flags&driver.PeerHasEndpoint != 0 {
|
|
peer.Endpoint.Port = p.Endpoint.Port()
|
|
peer.Endpoint.Host = p.Endpoint.Addr().String()
|
|
}
|
|
if p.Flags&driver.PeerHasPersistentKeepalive != 0 {
|
|
peer.PersistentKeepalive = p.PersistentKeepalive
|
|
}
|
|
peer.TxBytes = Bytes(p.TxBytes)
|
|
peer.RxBytes = Bytes(p.RxBytes)
|
|
if p.LastHandshake != 0 {
|
|
peer.LastHandshakeTime = HandshakeTime((p.LastHandshake - 116444736000000000) * 100)
|
|
}
|
|
var a *driver.AllowedIP
|
|
for j := uint32(0); j < p.AllowedIPsCount; j++ {
|
|
if a == nil {
|
|
a = p.FirstAllowedIP()
|
|
} else {
|
|
a = a.NextAllowedIP()
|
|
}
|
|
var ip netip.Addr
|
|
if a.AddressFamily == windows.AF_INET {
|
|
ip = netip.AddrFrom4(*(*[4]byte)(a.Address[:4]))
|
|
} else if a.AddressFamily == windows.AF_INET6 {
|
|
ip = netip.AddrFrom16(*(*[16]byte)(a.Address[:16]))
|
|
}
|
|
peer.AllowedIPs = append(peer.AllowedIPs, netip.PrefixFrom(ip, int(a.Cidr)))
|
|
}
|
|
conf.Peers = append(conf.Peers, peer)
|
|
}
|
|
return &conf
|
|
}
|