github.com/chwjbn/xclash@v0.2.0/config/config.go (about)

     1  package config
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"net/url"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/chwjbn/xclash/adapter"
    12  	"github.com/chwjbn/xclash/adapter/outbound"
    13  	"github.com/chwjbn/xclash/adapter/outboundgroup"
    14  	"github.com/chwjbn/xclash/adapter/provider"
    15  	"github.com/chwjbn/xclash/component/auth"
    16  	"github.com/chwjbn/xclash/component/fakeip"
    17  	"github.com/chwjbn/xclash/component/trie"
    18  	C "github.com/chwjbn/xclash/constant"
    19  	providerTypes "github.com/chwjbn/xclash/constant/provider"
    20  	"github.com/chwjbn/xclash/dns"
    21  	"github.com/chwjbn/xclash/log"
    22  	R "github.com/chwjbn/xclash/rule"
    23  	T "github.com/chwjbn/xclash/tunnel"
    24  
    25  	"gopkg.in/yaml.v2"
    26  )
    27  
    28  // General config
    29  type General struct {
    30  	Inbound
    31  	Controller
    32  	Mode      T.TunnelMode `json:"mode"`
    33  	LogLevel  log.LogLevel `json:"log-level"`
    34  	IPv6      bool         `json:"ipv6"`
    35  	Interface string       `json:"-"`
    36  }
    37  
    38  // Inbound
    39  type Inbound struct {
    40  	Port           int      `json:"port"`
    41  	SocksPort      int      `json:"socks-port"`
    42  	RedirPort      int      `json:"redir-port"`
    43  	TProxyPort     int      `json:"tproxy-port"`
    44  	MixedPort      int      `json:"mixed-port"`
    45  	Authentication []string `json:"authentication"`
    46  	AllowLan       bool     `json:"allow-lan"`
    47  	BindAddress    string   `json:"bind-address"`
    48  }
    49  
    50  // Controller
    51  type Controller struct {
    52  	ExternalController string `json:"-"`
    53  	ExternalUI         string `json:"-"`
    54  	Secret             string `json:"-"`
    55  }
    56  
    57  // DNS config
    58  type DNS struct {
    59  	Enable            bool             `yaml:"enable"`
    60  	IPv6              bool             `yaml:"ipv6"`
    61  	NameServer        []dns.NameServer `yaml:"nameserver"`
    62  	Fallback          []dns.NameServer `yaml:"fallback"`
    63  	FallbackFilter    FallbackFilter   `yaml:"fallback-filter"`
    64  	Listen            string           `yaml:"listen"`
    65  	EnhancedMode      C.DNSMode        `yaml:"enhanced-mode"`
    66  	DefaultNameserver []dns.NameServer `yaml:"default-nameserver"`
    67  	FakeIPRange       *fakeip.Pool
    68  	Hosts             *trie.DomainTrie
    69  	NameServerPolicy  map[string]dns.NameServer
    70  }
    71  
    72  // FallbackFilter config
    73  type FallbackFilter struct {
    74  	GeoIP     bool         `yaml:"geoip"`
    75  	GeoIPCode string       `yaml:"geoip-code"`
    76  	IPCIDR    []*net.IPNet `yaml:"ipcidr"`
    77  	Domain    []string     `yaml:"domain"`
    78  }
    79  
    80  // Profile config
    81  type Profile struct {
    82  	StoreSelected bool `yaml:"store-selected"`
    83  	StoreFakeIP   bool `yaml:"store-fake-ip"`
    84  }
    85  
    86  // Experimental config
    87  type Experimental struct{}
    88  
    89  // Config is clash config manager
    90  type Config struct {
    91  	General      *General
    92  	DNS          *DNS
    93  	Experimental *Experimental
    94  	Hosts        *trie.DomainTrie
    95  	Profile      *Profile
    96  	Rules        []C.Rule
    97  	Users        []auth.AuthUser
    98  	Proxies      map[string]C.Proxy
    99  	Providers    map[string]providerTypes.ProxyProvider
   100  }
   101  
   102  type RawDNS struct {
   103  	Enable            bool              `yaml:"enable"`
   104  	IPv6              bool              `yaml:"ipv6"`
   105  	UseHosts          bool              `yaml:"use-hosts"`
   106  	NameServer        []string          `yaml:"nameserver"`
   107  	Fallback          []string          `yaml:"fallback"`
   108  	FallbackFilter    RawFallbackFilter `yaml:"fallback-filter"`
   109  	Listen            string            `yaml:"listen"`
   110  	EnhancedMode      C.DNSMode         `yaml:"enhanced-mode"`
   111  	FakeIPRange       string            `yaml:"fake-ip-range"`
   112  	FakeIPFilter      []string          `yaml:"fake-ip-filter"`
   113  	DefaultNameserver []string          `yaml:"default-nameserver"`
   114  	NameServerPolicy  map[string]string `yaml:"nameserver-policy"`
   115  }
   116  
   117  type RawFallbackFilter struct {
   118  	GeoIP     bool     `yaml:"geoip"`
   119  	GeoIPCode string   `yaml:"geoip-code"`
   120  	IPCIDR    []string `yaml:"ipcidr"`
   121  	Domain    []string `yaml:"domain"`
   122  }
   123  
   124  type RawConfig struct {
   125  	Port               int          `yaml:"port"`
   126  	SocksPort          int          `yaml:"socks-port"`
   127  	RedirPort          int          `yaml:"redir-port"`
   128  	TProxyPort         int          `yaml:"tproxy-port"`
   129  	MixedPort          int          `yaml:"mixed-port"`
   130  	Authentication     []string     `yaml:"authentication"`
   131  	AllowLan           bool         `yaml:"allow-lan"`
   132  	BindAddress        string       `yaml:"bind-address"`
   133  	Mode               T.TunnelMode `yaml:"mode"`
   134  	LogLevel           log.LogLevel `yaml:"log-level"`
   135  	IPv6               bool         `yaml:"ipv6"`
   136  	ExternalController string       `yaml:"external-controller"`
   137  	ExternalUI         string       `yaml:"external-ui"`
   138  	Secret             string       `yaml:"secret"`
   139  	Interface          string       `yaml:"interface-name"`
   140  
   141  	ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"`
   142  	Hosts         map[string]string                 `yaml:"hosts"`
   143  	DNS           RawDNS                            `yaml:"dns"`
   144  	Experimental  Experimental                      `yaml:"experimental"`
   145  	Profile       Profile                           `yaml:"profile"`
   146  	Proxy         []map[string]interface{}          `yaml:"proxies"`
   147  	ProxyGroup    []map[string]interface{}          `yaml:"proxy-groups"`
   148  	Rule          []string                          `yaml:"rules"`
   149  }
   150  
   151  // Parse config
   152  func Parse(buf []byte) (*Config, error) {
   153  	rawCfg, err := UnmarshalRawConfig(buf)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	return ParseRawConfig(rawCfg)
   159  }
   160  
   161  func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
   162  	// config with default value
   163  	rawCfg := &RawConfig{
   164  		AllowLan:       false,
   165  		BindAddress:    "*",
   166  		Mode:           T.Rule,
   167  		Authentication: []string{},
   168  		LogLevel:       log.INFO,
   169  		Hosts:          map[string]string{},
   170  		Rule:           []string{},
   171  		Proxy:          []map[string]interface{}{},
   172  		ProxyGroup:     []map[string]interface{}{},
   173  		DNS: RawDNS{
   174  			Enable:      false,
   175  			UseHosts:    true,
   176  			FakeIPRange: "198.18.0.1/16",
   177  			FallbackFilter: RawFallbackFilter{
   178  				GeoIP:     true,
   179  				GeoIPCode: "CN",
   180  				IPCIDR:    []string{},
   181  			},
   182  			DefaultNameserver: []string{
   183  				"114.114.114.114",
   184  				"8.8.8.8",
   185  			},
   186  		},
   187  		Profile: Profile{
   188  			StoreSelected: true,
   189  		},
   190  	}
   191  
   192  	if err := yaml.Unmarshal(buf, rawCfg); err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	return rawCfg, nil
   197  }
   198  
   199  func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
   200  	config := &Config{}
   201  
   202  	config.Experimental = &rawCfg.Experimental
   203  	config.Profile = &rawCfg.Profile
   204  
   205  	general, err := parseGeneral(rawCfg)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	config.General = general
   210  
   211  	proxies, providers, err := parseProxies(rawCfg)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	config.Proxies = proxies
   216  	config.Providers = providers
   217  
   218  	rules, err := parseRules(rawCfg, proxies)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	config.Rules = rules
   223  
   224  	hosts, err := parseHosts(rawCfg)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  	config.Hosts = hosts
   229  
   230  	dnsCfg, err := parseDNS(rawCfg, hosts)
   231  	if err != nil {
   232  		return nil, err
   233  	}
   234  	config.DNS = dnsCfg
   235  
   236  	config.Users = parseAuthentication(rawCfg.Authentication)
   237  
   238  	return config, nil
   239  }
   240  
   241  func parseGeneral(cfg *RawConfig) (*General, error) {
   242  	externalUI := cfg.ExternalUI
   243  
   244  	// checkout externalUI exist
   245  	if externalUI != "" {
   246  		externalUI = C.Path.Resolve(externalUI)
   247  
   248  		if _, err := os.Stat(externalUI); os.IsNotExist(err) {
   249  			return nil, fmt.Errorf("external-ui: %s not exist", externalUI)
   250  		}
   251  	}
   252  
   253  	return &General{
   254  		Inbound: Inbound{
   255  			Port:        cfg.Port,
   256  			SocksPort:   cfg.SocksPort,
   257  			RedirPort:   cfg.RedirPort,
   258  			TProxyPort:  cfg.TProxyPort,
   259  			MixedPort:   cfg.MixedPort,
   260  			AllowLan:    cfg.AllowLan,
   261  			BindAddress: cfg.BindAddress,
   262  		},
   263  		Controller: Controller{
   264  			ExternalController: cfg.ExternalController,
   265  			ExternalUI:         cfg.ExternalUI,
   266  			Secret:             cfg.Secret,
   267  		},
   268  		Mode:      cfg.Mode,
   269  		LogLevel:  cfg.LogLevel,
   270  		IPv6:      cfg.IPv6,
   271  		Interface: cfg.Interface,
   272  	}, nil
   273  }
   274  
   275  func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[string]providerTypes.ProxyProvider, err error) {
   276  	proxies = make(map[string]C.Proxy)
   277  	providersMap = make(map[string]providerTypes.ProxyProvider)
   278  	proxyList := []string{}
   279  	proxiesConfig := cfg.Proxy
   280  	groupsConfig := cfg.ProxyGroup
   281  	providersConfig := cfg.ProxyProvider
   282  
   283  	proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect())
   284  	proxies["REJECT"] = adapter.NewProxy(outbound.NewReject())
   285  	proxyList = append(proxyList, "DIRECT", "REJECT")
   286  
   287  	// parse proxy
   288  	for idx, mapping := range proxiesConfig {
   289  		proxy, err := adapter.ParseProxy(mapping)
   290  		if err != nil {
   291  			return nil, nil, fmt.Errorf("proxy %d: %w", idx, err)
   292  		}
   293  
   294  		if _, exist := proxies[proxy.Name()]; exist {
   295  			return nil, nil, fmt.Errorf("proxy %s is the duplicate name", proxy.Name())
   296  		}
   297  		proxies[proxy.Name()] = proxy
   298  		proxyList = append(proxyList, proxy.Name())
   299  	}
   300  
   301  	// keep the original order of ProxyGroups in config file
   302  	for idx, mapping := range groupsConfig {
   303  		groupName, existName := mapping["name"].(string)
   304  		if !existName {
   305  			return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
   306  		}
   307  		proxyList = append(proxyList, groupName)
   308  	}
   309  
   310  	// check if any loop exists and sort the ProxyGroups
   311  	if err := proxyGroupsDagSort(groupsConfig); err != nil {
   312  		return nil, nil, err
   313  	}
   314  
   315  	// parse and initial providers
   316  	for name, mapping := range providersConfig {
   317  		if name == provider.ReservedName {
   318  			return nil, nil, fmt.Errorf("can not defined a provider called `%s`", provider.ReservedName)
   319  		}
   320  
   321  		pd, err := provider.ParseProxyProvider(name, mapping)
   322  		if err != nil {
   323  			return nil, nil, fmt.Errorf("parse proxy provider %s error: %w", name, err)
   324  		}
   325  
   326  		providersMap[name] = pd
   327  	}
   328  
   329  	for _, provider := range providersMap {
   330  		log.Infoln("Start initial provider %s", provider.Name())
   331  		if err := provider.Initial(); err != nil {
   332  			return nil, nil, fmt.Errorf("initial proxy provider %s error: %w", provider.Name(), err)
   333  		}
   334  	}
   335  
   336  	// parse proxy group
   337  	for idx, mapping := range groupsConfig {
   338  		group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap)
   339  		if err != nil {
   340  			return nil, nil, fmt.Errorf("proxy group[%d]: %w", idx, err)
   341  		}
   342  
   343  		groupName := group.Name()
   344  		if _, exist := proxies[groupName]; exist {
   345  			return nil, nil, fmt.Errorf("proxy group %s: the duplicate name", groupName)
   346  		}
   347  
   348  		proxies[groupName] = adapter.NewProxy(group)
   349  	}
   350  
   351  	// initial compatible provider
   352  	for _, pd := range providersMap {
   353  		if pd.VehicleType() != providerTypes.Compatible {
   354  			continue
   355  		}
   356  
   357  		log.Infoln("Start initial compatible provider %s", pd.Name())
   358  		if err := pd.Initial(); err != nil {
   359  			return nil, nil, err
   360  		}
   361  	}
   362  
   363  	ps := []C.Proxy{}
   364  	for _, v := range proxyList {
   365  		ps = append(ps, proxies[v])
   366  	}
   367  	hc := provider.NewHealthCheck(ps, "", 0, true)
   368  	pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc)
   369  	providersMap[provider.ReservedName] = pd
   370  
   371  	global := outboundgroup.NewSelector(
   372  		&outboundgroup.GroupCommonOption{
   373  			Name: "GLOBAL",
   374  		},
   375  		[]providerTypes.ProxyProvider{pd},
   376  	)
   377  	proxies["GLOBAL"] = adapter.NewProxy(global)
   378  	return proxies, providersMap, nil
   379  }
   380  
   381  func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) {
   382  	rules := []C.Rule{}
   383  	rulesConfig := cfg.Rule
   384  
   385  	// parse rules
   386  	for idx, line := range rulesConfig {
   387  		rule := trimArr(strings.Split(line, ","))
   388  		var (
   389  			payload string
   390  			target  string
   391  			params  = []string{}
   392  		)
   393  
   394  		switch l := len(rule); {
   395  		case l == 2:
   396  			target = rule[1]
   397  		case l == 3:
   398  			payload = rule[1]
   399  			target = rule[2]
   400  		case l >= 4:
   401  			payload = rule[1]
   402  			target = rule[2]
   403  			params = rule[3:]
   404  		default:
   405  			return nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line)
   406  		}
   407  
   408  		if _, ok := proxies[target]; !ok {
   409  			return nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target)
   410  		}
   411  
   412  		rule = trimArr(rule)
   413  		params = trimArr(params)
   414  
   415  		parsed, parseErr := R.ParseRule(rule[0], payload, target, params)
   416  		if parseErr != nil {
   417  			return nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error())
   418  		}
   419  
   420  		rules = append(rules, parsed)
   421  	}
   422  
   423  	return rules, nil
   424  }
   425  
   426  func parseHosts(cfg *RawConfig) (*trie.DomainTrie, error) {
   427  	tree := trie.New()
   428  
   429  	// add default hosts
   430  	if err := tree.Insert("localhost", net.IP{127, 0, 0, 1}); err != nil {
   431  		log.Errorln("insert localhost to host error: %s", err.Error())
   432  	}
   433  
   434  	if len(cfg.Hosts) != 0 {
   435  		for domain, ipStr := range cfg.Hosts {
   436  			ip := net.ParseIP(ipStr)
   437  			if ip == nil {
   438  				return nil, fmt.Errorf("%s is not a valid IP", ipStr)
   439  			}
   440  			tree.Insert(domain, ip)
   441  		}
   442  	}
   443  
   444  	return tree, nil
   445  }
   446  
   447  func hostWithDefaultPort(host string, defPort string) (string, error) {
   448  	if !strings.Contains(host, ":") {
   449  		host += ":"
   450  	}
   451  
   452  	hostname, port, err := net.SplitHostPort(host)
   453  	if err != nil {
   454  		return "", err
   455  	}
   456  
   457  	if port == "" {
   458  		port = defPort
   459  	}
   460  
   461  	return net.JoinHostPort(hostname, port), nil
   462  }
   463  
   464  func parseNameServer(servers []string) ([]dns.NameServer, error) {
   465  	nameservers := []dns.NameServer{}
   466  
   467  	for idx, server := range servers {
   468  		// parse without scheme .e.g 8.8.8.8:53
   469  		if !strings.Contains(server, "://") {
   470  			server = "udp://" + server
   471  		}
   472  		u, err := url.Parse(server)
   473  		if err != nil {
   474  			return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
   475  		}
   476  
   477  		var addr, dnsNetType string
   478  		switch u.Scheme {
   479  		case "udp":
   480  			addr, err = hostWithDefaultPort(u.Host, "53")
   481  			dnsNetType = "" // UDP
   482  		case "tcp":
   483  			addr, err = hostWithDefaultPort(u.Host, "53")
   484  			dnsNetType = "tcp" // TCP
   485  		case "tls":
   486  			addr, err = hostWithDefaultPort(u.Host, "853")
   487  			dnsNetType = "tcp-tls" // DNS over TLS
   488  		case "https":
   489  			clearURL := url.URL{Scheme: "https", Host: u.Host, Path: u.Path}
   490  			addr = clearURL.String()
   491  			dnsNetType = "https" // DNS over HTTPS
   492  		case "dhcp":
   493  			addr = u.Host
   494  			dnsNetType = "dhcp" // UDP from DHCP
   495  		default:
   496  			return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme)
   497  		}
   498  
   499  		if err != nil {
   500  			return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
   501  		}
   502  
   503  		nameservers = append(
   504  			nameservers,
   505  			dns.NameServer{
   506  				Net:  dnsNetType,
   507  				Addr: addr,
   508  			},
   509  		)
   510  	}
   511  	return nameservers, nil
   512  }
   513  
   514  func parseNameServerPolicy(nsPolicy map[string]string) (map[string]dns.NameServer, error) {
   515  	policy := map[string]dns.NameServer{}
   516  
   517  	for domain, server := range nsPolicy {
   518  		nameservers, err := parseNameServer([]string{server})
   519  		if err != nil {
   520  			return nil, err
   521  		}
   522  		if _, valid := trie.ValidAndSplitDomain(domain); !valid {
   523  			return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain)
   524  		}
   525  		policy[domain] = nameservers[0]
   526  	}
   527  
   528  	return policy, nil
   529  }
   530  
   531  func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) {
   532  	ipNets := []*net.IPNet{}
   533  
   534  	for idx, ip := range ips {
   535  		_, ipnet, err := net.ParseCIDR(ip)
   536  		if err != nil {
   537  			return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %s", idx, err.Error())
   538  		}
   539  		ipNets = append(ipNets, ipnet)
   540  	}
   541  
   542  	return ipNets, nil
   543  }
   544  
   545  func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie) (*DNS, error) {
   546  	cfg := rawCfg.DNS
   547  	if cfg.Enable && len(cfg.NameServer) == 0 {
   548  		return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
   549  	}
   550  
   551  	dnsCfg := &DNS{
   552  		Enable:       cfg.Enable,
   553  		Listen:       cfg.Listen,
   554  		IPv6:         cfg.IPv6,
   555  		EnhancedMode: cfg.EnhancedMode,
   556  		FallbackFilter: FallbackFilter{
   557  			IPCIDR: []*net.IPNet{},
   558  		},
   559  	}
   560  	var err error
   561  	if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer); err != nil {
   562  		return nil, err
   563  	}
   564  
   565  	if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback); err != nil {
   566  		return nil, err
   567  	}
   568  
   569  	if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy); err != nil {
   570  		return nil, err
   571  	}
   572  
   573  	if len(cfg.DefaultNameserver) == 0 {
   574  		return nil, errors.New("default nameserver should have at least one nameserver")
   575  	}
   576  	if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver); err != nil {
   577  		return nil, err
   578  	}
   579  	// check default nameserver is pure ip addr
   580  	for _, ns := range dnsCfg.DefaultNameserver {
   581  		host, _, err := net.SplitHostPort(ns.Addr)
   582  		if err != nil || net.ParseIP(host) == nil {
   583  			return nil, errors.New("default nameserver should be pure IP")
   584  		}
   585  	}
   586  
   587  	if cfg.EnhancedMode == C.DNSFakeIP {
   588  		_, ipnet, err := net.ParseCIDR(cfg.FakeIPRange)
   589  		if err != nil {
   590  			return nil, err
   591  		}
   592  
   593  		var host *trie.DomainTrie
   594  		// fake ip skip host filter
   595  		if len(cfg.FakeIPFilter) != 0 {
   596  			host = trie.New()
   597  			for _, domain := range cfg.FakeIPFilter {
   598  				host.Insert(domain, true)
   599  			}
   600  		}
   601  
   602  		pool, err := fakeip.New(fakeip.Options{
   603  			IPNet:       ipnet,
   604  			Size:        1000,
   605  			Host:        host,
   606  			Persistence: rawCfg.Profile.StoreFakeIP,
   607  		})
   608  		if err != nil {
   609  			return nil, err
   610  		}
   611  
   612  		dnsCfg.FakeIPRange = pool
   613  	}
   614  
   615  	dnsCfg.FallbackFilter.GeoIP = cfg.FallbackFilter.GeoIP
   616  	dnsCfg.FallbackFilter.GeoIPCode = cfg.FallbackFilter.GeoIPCode
   617  	if fallbackip, err := parseFallbackIPCIDR(cfg.FallbackFilter.IPCIDR); err == nil {
   618  		dnsCfg.FallbackFilter.IPCIDR = fallbackip
   619  	}
   620  	dnsCfg.FallbackFilter.Domain = cfg.FallbackFilter.Domain
   621  
   622  	if cfg.UseHosts {
   623  		dnsCfg.Hosts = hosts
   624  	}
   625  
   626  	return dnsCfg, nil
   627  }
   628  
   629  func parseAuthentication(rawRecords []string) []auth.AuthUser {
   630  	users := make([]auth.AuthUser, 0)
   631  	for _, line := range rawRecords {
   632  		userData := strings.SplitN(line, ":", 2)
   633  		if len(userData) == 2 {
   634  			users = append(users, auth.AuthUser{User: userData[0], Pass: userData[1]})
   635  		}
   636  	}
   637  	return users
   638  }