package main import ( "bufio" "fmt" "os" "strings" ) // Error is a parse error recording the filename and line number. type Error struct { File string Line int Err error } func (e Error) Error() string { if e.Line < 1 { return fmt.Sprintf("%v: %v", e.File, e.Err) } return fmt.Sprintf("%v:%v: %v", e.File, e.Line, e.Err) } func parseConfigLine(line string) (key, val string) { line = strings.TrimRight(line, "\t ") keylen := strings.IndexAny(line, "\t =") if keylen < 0 { keylen = len(line) } variable := line[:keylen] value := strings.TrimLeft(line[keylen:], "\t ") if strings.HasPrefix(value, "=") { value = strings.TrimLeft(value[1:], "\t ") } return variable, value } // ReadConfigFile opens and reads a Tinc configuration file, returning // a map of settings. Since Tinc setting names are case-insensitive, // the keys in the map are all lower-case. func ReadConfigFile(fname string) (map[string][]string, error) { cfg := make(map[string][]string) fp, err := os.Open(fname) if err != nil { return nil, Error{File: fname, Err: err} } buffer := bufio.NewScanner(fp) lineno := 0 ignore := false for buffer.Scan() { line := buffer.Text() lineno++ if len(line) == 0 || line[0] == '#' { continue } if ignore { if strings.HasPrefix(line, "-----END") { ignore = false } continue } if strings.HasPrefix(line, "-----BEGIN") { ignore = true continue } variable, value := parseConfigLine(line) if len(value) == 0 { err = fmt.Errorf("No value for variable: %s", variable) return nil, Error{File: fname, Line: lineno, Err: err} } variable = strings.ToLower(variable) cfg[variable] = append(cfg[variable], value) } err = buffer.Err() if err != nil { return nil, Error{File: fname, Err: err} } return cfg, nil } // GetAddresses inspects a tinc host-confing settings object and // returns a list of public addresses in it. func GetAddresses(cfg map[string][]string) []struct{ Node, Port string } { var result []struct{ Node, Port string } for _, node := range cfg["address"] { var port string space := strings.IndexByte(node, ' ') if space < 0 { if ports, portsOk := cfg["port"]; portsOk { port = ports[0] } else { port = "655" } } else { port = node[space+1:] node = node[:space] } result = append(result, struct{ Node, Port string }{node, port}) } return result }