func parse(ic ini.Config) (cp *Config, err error) { c := Config{} c.General.Other = make(map[string]string) c.HostsMap = make(map[string]int) c.Comments = ic.Comments("") for _, section := range ic.Sections() { options := ic.OptionMap(section) if section == "general" { err := parseGeneral(&c, options) if err != nil { return nil, err } c.General.Comments = ic.Comments(section) } else if strings.HasPrefix(section, "hosts.") { host, err := parseHost(ic, section) if err != nil { return nil, err } if c.General.Version < 400 { for k := range host.Other { return nil, fmt.Errorf("unrecognized field %q on host %q not permitted by config version %d", k, host.Name, c.General.Version) } } c.Hosts = append(c.Hosts, host) c.HostsMap[host.Name] = len(c.Hosts) - 1 } else if strings.HasPrefix(section, "forwards.") { forw, err := parseForward(ic, section) if err != nil { return nil, err } if cmt := ic.Get(section, "comment"); cmt != "" && c.General.Version < 320 { return nil, fmt.Errorf("forward comments are supported in config version 3.2 and above") } c.Forwards = append(c.Forwards, forw) } else if section == "openconnect" { c.OpenConnect = options } else if section == "vpnc" { c.Vpnc = options } else if section == "vpn routes" { for net, mask := range options { c.VpnRoutes = append(c.VpnRoutes, net+"/"+mask) } sort.Strings(c.VpnRoutes) } } // Check for existence of either hosts or forwards if len(c.Hosts) == 0 && len(c.Forwards) == 0 { err = fmt.Errorf(`must exist either "hosts" or "forwards" section`) return } // Check for nonexistant "main" if c.General.Main != "" { if _, ok := c.HostsMap[c.General.Main]; !ok { err = fmt.Errorf(`"main" refers to nonexistent host %q`, c.General.Main) return } } for _, host := range c.Hosts { // Check for errors in "via" links if host.Via != "" { if _, ok := c.HostsMap[host.Via]; !ok { err = fmt.Errorf(`host %q "via" refers to nonexistent host %q`, host.Name, host.Via) return } } } seenSources := map[string]bool{} for _, fwd := range c.Forwards { for _, line := range fwd.Lines { // Check for duplicate forwards for i := 0; i <= line.Repeat; i++ { src := line.SrcString(i) if seenSources[src] { err = fmt.Errorf("duplicate forward source %q", src) return } seenSources[src] = true } // Check for privileged ports if line.SrcPort < 1024 { err = fmt.Errorf("privileged source port %d in forward source %q", line.SrcPort, line.SrcString(0)) return } } } cp = &c return }