package parser import ( "bufio" "bytes" "errors" "io" "net/netip" "strings" "github.com/miekg/dns" ) var ErrNoParser = errors.New("no suitable parser could be found") type Parser interface { CanHandle(line string) bool } type DomainsParser interface { Parser ParseDomains(io.Reader) (domains []string, ignored int, err error) } type NetworksParser interface { Parser ParseNetworks(io.Reader) (prefixes []netip.Prefix, ignored int, err error) } var ( domainsParsers []DomainsParser networksParsers []NetworksParser ) func RegisterDomainsParser(parser DomainsParser) { domainsParsers = append(domainsParsers, parser) } func RegisterNetworksParser(parser NetworksParser) { networksParsers = append(networksParsers, parser) } func ParseDomains(r io.Reader) (domains []string, ignored int, err error) { var ( buffer = new(bytes.Buffer) scanner = bufio.NewScanner(io.TeeReader(r, buffer)) line string parser DomainsParser ) for scanner.Scan() { line = strings.TrimSpace(scanner.Text()) if isComment(line) { continue } for _, parser = range domainsParsers { if parser.CanHandle(line) { // log.Printf("using parser %T", parser) return parser.ParseDomains(io.MultiReader(buffer, r)) } } break } return nil, 0, ErrNoParser } func ParseNetworks(r io.Reader) (prefixes []netip.Prefix, ignored int, err error) { var ( buffer = new(bytes.Buffer) scanner = bufio.NewScanner(io.TeeReader(r, buffer)) line string parser NetworksParser ) for scanner.Scan() { line = strings.TrimSpace(scanner.Text()) if isComment(line) { continue } for _, parser = range networksParsers { if parser.CanHandle(line) { // log.Printf("using parser %T", parser) return parser.ParseNetworks(io.MultiReader(buffer, r)) } } break } return nil, 0, ErrNoParser } func isComment(line string) bool { return line == "" || line[0] == '#' || line[0] == '!' } func isDomainName(s string) bool { n, ok := dns.IsDomainName(s) return n >= 2 && ok } func isIP(s string) bool { _, err := netip.ParseAddr(s) return err == nil } func isPrefix(s string) bool { _, err := netip.ParsePrefix(s) return err == nil } func addrToPrefix(addr netip.Addr) netip.Prefix { switch { case addr.Is4(): prefix, _ := addr.Prefix(32) return prefix case addr.Is4In6(): prefix, _ := addr.Unmap().Prefix(32) return prefix case addr.Is6(): prefix, _ := addr.Prefix(128) return prefix default: return netip.Prefix{} } }