diff options
Diffstat (limited to 'pkg/wgconf')
| -rw-r--r-- | pkg/wgconf/wgconf.go | 97 | ||||
| -rw-r--r-- | pkg/wgconf/wgconf_test.go | 66 |
2 files changed, 158 insertions, 5 deletions
diff --git a/pkg/wgconf/wgconf.go b/pkg/wgconf/wgconf.go new file mode 100644 index 0000000..2615892 --- /dev/null +++ b/pkg/wgconf/wgconf.go @@ -0,0 +1,97 @@ +package wgconf + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +// Config represents a parsed WireGuard configuration file. +type Config struct { + PrivateKey string + Address string + DNS string + Peers []Peer +} + +// Peer represents a WireGuard peer. +type Peer struct { + PublicKey string + Endpoint string + AllowedIPs []string +} + +// Parse reads a WireGuard .conf file and returns a Config struct. +func Parse(path string) (*Config, error) { + file, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to open config file: %w", err) + } + defer func() { + if err := file.Close(); err != nil { + // We use a simple print here because we are in a defer + fmt.Printf("warning: failed to close config file %s: %v\n", path, err) + } + }() + + cfg := &Config{} + var currentPeer *Peer + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + if strings.HasPrefix(line, "[") { + section := strings.Trim(line, "[]") + if section == "Peer" { + if currentPeer != nil { + cfg.Peers = append(cfg.Peers, *currentPeer) + } + currentPeer = &Peer{} + } + continue + } + + parts := strings.SplitN(line, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid line format: %s", line) + } + + key := strings.TrimSpace(parts[0]) + val := strings.TrimSpace(parts[1]) + + if currentPeer != nil { + switch key { + case "PublicKey": + currentPeer.PublicKey = val + case "Endpoint": + currentPeer.Endpoint = val + case "AllowedIPs": + currentPeer.AllowedIPs = strings.Split(val, ",") + } + } else { + switch key { + case "PrivateKey": + cfg.PrivateKey = val + case "Address": + cfg.Address = val + case "DNS": + cfg.DNS = val + } + } + } + + if currentPeer != nil { + cfg.Peers = append(cfg.Peers, *currentPeer) + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading config file: %w", err) + } + + return cfg, nil +} diff --git a/pkg/wgconf/wgconf_test.go b/pkg/wgconf/wgconf_test.go index d0bcb0b..805aeaa 100644 --- a/pkg/wgconf/wgconf_test.go +++ b/pkg/wgconf/wgconf_test.go @@ -1,15 +1,71 @@ package wgconf import ( + "os" + "path/filepath" "testing" ) func TestParseConfig(t *testing.T) { - // Test that valid .conf files are parsed correctly and invalid ones return errors. - t.Skip("not implemented") + content := `[Interface] +PrivateKey = ABC123XYZ +Address = 10.0.0.1/24 +DNS = 1.1.1.1 + +[Peer] +PublicKey = DEF456UVW +Endpoint = 1.2.3.4:51820 +AllowedIPs = 0.0.0.0/0 + +[Peer] +PublicKey = GHI789TSR +Endpoint = 5.6.7.8:51820 +AllowedIPs = 192.168.1.0/24, 192.168.2.0/24` + + tmpFile := filepath.Join(t.TempDir(), "test.conf") + if err := os.WriteFile(tmpFile, []byte(content), 0644); err != nil { + t.Fatal(err) + } + + cfg, err := Parse(tmpFile) + if err != nil { + t.Fatalf("Parse failed: %v", err) + } + + if cfg.PrivateKey != "ABC123XYZ" { + t.Errorf("expected PrivateKey ABC123XYZ, got %s", cfg.PrivateKey) + } + if cfg.Address != "10.0.0.1/24" { + t.Errorf("expected Address 10.0.0.1/24, got %s", cfg.Address) + } + if cfg.DNS != "1.1.1.1" { + t.Errorf("expected DNS 1.1.1.1, got %s", cfg.DNS) + } + if len(cfg.Peers) != 2 { + t.Fatalf("expected 2 peers, got %d", len(cfg.Peers)) + } + + p1 := cfg.Peers[0] + if p1.PublicKey != "DEF456UVW" || p1.Endpoint != "1.2.3.4:51820" || len(p1.AllowedIPs) != 1 || p1.AllowedIPs[0] != "0.0.0.0/0" { + t.Errorf("Peer 1 mismatch: %+v", p1) + } + + p2 := cfg.Peers[1] + if p2.PublicKey != "GHI789TSR" || p2.Endpoint != "5.6.7.8:51820" || len(p2.AllowedIPs) != 2 || p2.AllowedIPs[0] != "192.168.1.0/24" { + t.Errorf("Peer 2 mismatch: %+v", p2) + } } -func TestValidateProfile(t *testing.T) { - // Test that profile names are resolved correctly to ~/.config/wg-wrap/profiles/*.conf. - t.Skip("not implemented") +func TestParseInvalidConfig(t *testing.T) { + content := `[Interface] +InvalidLineWithoutEquals` + tmpFile := filepath.Join(t.TempDir(), "invalid.conf") + if err := os.WriteFile(tmpFile, []byte(content), 0644); err != nil { + t.Fatal(err) + } + + _, err := Parse(tmpFile) + if err == nil { + t.Error("expected error for invalid line format, got nil") + } } |
