/* SPDX-License-Identifier: MIT * * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. * * From https://git.zx2c4.com/wireguard-windows/conf/ */ package main import ( "crypto/subtle" "encoding/base64" "encoding/hex" "fmt" "net" "strconv" "strings" "time" ) const KeyLength = 32 type IPCidr struct { IP net.IP Cidr uint8 } type Endpoint struct { Host string Port uint16 } type Key [KeyLength]byte type HandshakeTime time.Duration type Bytes uint64 type Config struct { Name string Interface Interface Peers []Peer } type Interface struct { PrivateKey Key MyLogin string KeySign string Addresses []IPCidr ListenPort uint16 MTU uint16 DNS []net.IP DNSSearch []string PreUp string PostUp string PreDown string PostDown string } type Peer struct { PublicKey Key PresharedKey Key AllowedIPs []IPCidr Endpoint Endpoint PersistentKeepalive uint16 RxBytes Bytes TxBytes Bytes LastHandshakeTime HandshakeTime } func (k *Key) IsZero() bool { var zeros Key return subtle.ConstantTimeCompare(zeros[:], k[:]) == 1 } type ParseError struct { why string offender string } func (e *ParseError) Error() string { return fmt.Sprintf("%s: %q", e.why, e.offender) } func parseIPCidr(s string) (ipcidr *IPCidr, err error) { var addrStr, cidrStr string var cidr int i := strings.IndexByte(s, '/') if i < 0 { addrStr = s } else { addrStr, cidrStr = s[:i], s[i+1:] } err = &ParseError{fmt.Sprintf("Invalid IP address"), s} addr := net.ParseIP(addrStr) if addr == nil { return } maybeV4 := addr.To4() if maybeV4 != nil { addr = maybeV4 } if len(cidrStr) > 0 { err = &ParseError{fmt.Sprintf("Invalid network prefix length"), s} cidr, err = strconv.Atoi(cidrStr) if err != nil || cidr < 0 || cidr > 128 { return } if cidr > 32 && maybeV4 != nil { return } } else { if maybeV4 != nil { cidr = 32 } else { cidr = 128 } } return &IPCidr{addr, uint8(cidr)}, nil } func parseEndpoint(s string) (*Endpoint, error) { i := strings.LastIndexByte(s, ':') if i < 0 { return nil, &ParseError{fmt.Sprintf("Missing port from endpoint"), s} } host, portStr := s[:i], s[i+1:] if len(host) < 1 { return nil, &ParseError{fmt.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{fmt.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 := net.ParseIP(host[1:end]) if maybeV6 == nil || len(maybeV6) != net.IPv6len { return nil, err } } else { return nil, err } host = host[1 : len(host)-1] } return &Endpoint{host, uint16(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{fmt.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{fmt.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{fmt.Sprintf("Invalid persistent keepalive"), s} } return uint16(m), nil } func parseKeyBase64(s string) (*Key, error) { k, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, &ParseError{fmt.Sprintf("Invalid key: %v", err), s} } if len(k) != KeyLength { return nil, &ParseError{fmt.Sprintf("Keys must decode to exactly 32 bytes"), s} } var key Key copy(key[:], k) return &key, nil } func parseKeyHex(s string) (*Key, error) { k, err := hex.DecodeString(s) if err != nil { return nil, &ParseError{fmt.Sprintf("Invalid key: %v", err), s} } if len(k) != KeyLength { return nil, &ParseError{fmt.Sprintf("Keys must decode to exactly 32 bytes"), s} } var key Key copy(key[:], k) return &key, nil } func parseBytesOrStamp(s string) (uint64, error) { b, err := strconv.ParseUint(s, 10, 64) if err != nil { return 0, &ParseError{fmt.Sprintf("Number must be a number between 0 and 2^64-1: %v", err), s} } return b, 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{fmt.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 string, name string) (*Config, error) { lines := strings.Split(s, "\n") parserState := notInASection conf := Config{Name: name} sawPrivateKey := false var peer *Peer for _, line := range lines { pound := strings.IndexByte(line, '#') if pound >= 0 { if !strings.Contains(line, "MyLogin") && !strings.Contains(line, "KeySign") { line = line[:pound] } else { line = line[pound+1:] } } 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{fmt.Sprintf("Line must occur in a section"), line} } equals := strings.IndexByte(line, '=') if equals < 0 { return nil, &ParseError{fmt.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{fmt.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 := net.ParseIP(address) if a == 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 default: return nil, &ParseError{fmt.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 case "mylogin": conf.Interface.MyLogin = val case "keysign": conf.Interface.KeySign = val default: return nil, &ParseError{fmt.Sprintf("Invalid key for [Peer] section"), key} } } } conf.maybeAddPeer(peer) if !sawPrivateKey { return nil, &ParseError{fmt.Sprintf("An interface must have a private key"), fmt.Sprintf("[none specified]")} } for _, p := range conf.Peers { if p.PublicKey.IsZero() { return nil, &ParseError{fmt.Sprintf("All peers must have public keys"), fmt.Sprintf("[none specified]")} } } return &conf, nil }