func Fuzz(data []byte) int { for _, v := range data { if v <= 0x20 || v >= 0x80 { return 0 } } dec, err := idna.ToUnicode(string(data)) if err != nil { return 0 } enc, err := idna.ToASCII(dec) if err != nil { fmt.Printf("data: %q\n", data) fmt.Printf("dec : %q\n", dec) panic(err) } dec1, err := idna.ToUnicode(enc) if err != nil { fmt.Printf("data: %q\n", data) fmt.Printf("dec : %q\n", dec) fmt.Printf("enc : %q\n", enc) panic(err) } if dec != dec1 { fmt.Printf("data: %q\n", data) fmt.Printf("dec : %q\n", dec) fmt.Printf("enc : %q\n", enc) fmt.Printf("dec1: %q\n", dec1) panic("not equal") } return 1 }
// Match returns the length of the ETLD matched, if no match, then 0 is returned. func Match(s string) int { s, err := idna.ToUnicode(strings.ToLower(s)) if err != nil { return 0 } return etldlen(s) }
// ReadMetadata reads JSON files for each zone in the metadata directory. func ReadMetadata(zones map[string]*Zone) (errs []error) { dir := filepath.Join(BaseDir, "metadata") paths, _ := filepath.Glob(filepath.Join(dir, "*.json")) var read int for _, path := range paths { // Ensure filename equals ASCII/punycode domain name base := filepath.Base(path) ext := filepath.Ext(base) di := strings.TrimSuffix(base, ext) d, err := idna.ToUnicode(di) if err != nil { errs = append(errs, err) LogError(err) continue } // Ensure the domain exists in zones.txt z, ok := zones[d] if !ok { err = fmt.Errorf("domain not found in zones.txt: %s", base) errs = append(errs, err) LogWarning(err) continue } // Parse the JSON metadata f, err := os.Open(path) if err != nil { err = fmt.Errorf("cannot load %s: %s", base, err) errs = append(errs, err) LogError(err) continue } dec := json.NewDecoder(f) err = dec.Decode(z) f.Close() if err != nil && err != io.EOF { err = fmt.Errorf("unable to parse %s: %s", base, err) errs = append(errs, err) LogError(err) continue } read++ // Ensure domain name matches if z.Domain != d { err = fmt.Errorf("domain %s doesn’t match filename (expected %s): %s", z.Domain, z.ASCII(), base) errs = append(errs, err) LogError(err) continue } } color.Fprintf(os.Stderr, "@{.}Read %d metadata files\n", read) return }
// parseDomain parses the domain part of a jid according to RFC7622 func parseDomain(domain string) string { ip := net.ParseIP(domain) if ip != nil { return ip.String() } domain, err := idna.ToUnicode(domain) if err != nil { return "" } return domain }
// decodes IDN names to Unicode and adds it to value func decodeIDN(field mapper.Field) mapper.Field { for _, item := range field.Value { idnItem, err := idna.ToUnicode(item) if err == nil && idnItem != item { field.Value = append( field.Value, strings.Replace(field.Format, "{idn}", idnItem, 1), ) } } field.Format = "" return field }
// Normalize returns normalized URL string. // Behavior: // 1. Remove unnecessary host dots. // 2. Remove default port (http://localhost:80 becomes http://localhost). // 3. Remove duplicate slashes. // 4. Remove unnecessary dots from path. // 5. Sort query parameters. // 6. Decode host IP into decimal numbers. // 7. Handle escape values. // 8. Decode Punycode domains into UTF8 representation. func Normalize(u *url.URL) (string, error) { host, port, err := SplitHostPort(u) if err != nil { return "", err } if err := checkHost(host); err != nil { return "", err } // Decode Punycode. host, err = idna.ToUnicode(host) if err != nil { return "", err } u.Host = strings.ToLower(host) if port != "" { u.Host += ":" + port } u.Scheme = strings.ToLower(u.Scheme) return purell.NormalizeURL(u, normalizeFlags), nil }
// MatchingRules returns a list of the rules that u matches. // For consistency with phrase matching, it is a map with rules for keys // and with all values equal to 1. func (m *URLMatcher) MatchingRules(u *url.URL) map[rule]int { result := make(map[rule]int) host := strings.ToLower(u.Host) // strip off the port number, if present colon := strings.LastIndex(host, ":") // IPv6 addresses contain colons inside brackets, so be careful. if colon != -1 && !strings.Contains(host[colon:], "]") { host = host[:colon] } // Find the main domain name (e.g. "google" in "www.google.com"). suffix := publicsuffix.List.PublicSuffix(host) if suffix != "" && suffix != host { domain := host[:len(host)-len(suffix)-1] dot := strings.LastIndex(domain, ".") if dot != -1 { domain = domain[dot+1:] } if idn, err := idna.ToUnicode(domain); err == nil { domain = idn } m.domainRegexes.findMatches(domain, result) } if idn, err := idna.ToUnicode(host); err == nil { host = idn } urlString := "" if u.Scheme != "" { urlString += strings.ToLower(u.Scheme) + ":" } if host != "" { urlString += "//" + host m.hostRegexes.findMatches(host, result) } path := strings.ToLower(u.Path) m.pathRegexes.findMatches(path, result) urlString += path query := strings.ToLower(u.RawQuery) if query != "" { q, err := url.QueryUnescape(query) if err == nil { // Change ' ' back to '+'. query = strings.Replace(q, " ", "+", -1) } m.queryRegexes.findMatches(query, result) urlString += "?" + query } m.regexes.findMatches(urlString, result) // Test for matches of the host and of the domains it belongs to. s := host for { // Test for matches with the path. s2 := s + path for { if r, ok := m.fragments[s2]; ok { result[r] = 1 } slash := strings.LastIndex(s2, "/") if slash < 1 { // It's either not found, or at the first character. break } s2 = s2[:slash] } if r, ok := m.fragments[s]; ok { result[r] = 1 } dot := strings.Index(s, ".") if dot == -1 { break } s = s[dot+1:] } return result }
func (z *Zone) unicodeLabels(domain string) []string { prefix, _ := idna.ToUnicode(domain[:len(domain)-len(z.Domain)-1]) return strings.Split(prefix, ".") }
// WillingToIssue determines whether the CA is willing to issue for the provided // identifier. It expects domains in id to be lowercase to prevent mismatched // cases breaking queries. // // We place several criteria on identifiers we are willing to issue for: // // * MUST self-identify as DNS identifiers // * MUST contain only bytes in the DNS hostname character set // * MUST NOT have more than maxLabels labels // * MUST follow the DNS hostname syntax rules in RFC 1035 and RFC 2181 // In particular: // * MUST NOT contain underscores // * MUST NOT contain IDN labels (xn--) // * MUST NOT match the syntax of an IP address // * MUST end in a public suffix // * MUST have at least one label in addition to the public suffix // * MUST NOT be a label-wise suffix match for a name on the black list, // where comparison is case-independent (normalized to lower case) // // If WillingToIssue returns an error, it will be of type MalformedRequestError. func (pa *AuthorityImpl) WillingToIssue(id core.AcmeIdentifier) error { if id.Type != core.IdentifierDNS { return errInvalidIdentifier } domain := id.Value if domain == "" { return errEmptyName } for _, ch := range []byte(domain) { if !isDNSCharacter(ch) { return errInvalidDNSCharacter } } if len(domain) > maxDNSIdentifierLength { return errNameTooLong } if ip := net.ParseIP(domain); ip != nil { return errIPAddress } if strings.HasSuffix(domain, ".") { return errNameEndsInDot } labels := strings.Split(domain, ".") if len(labels) > maxLabels { return errTooManyLabels } if len(labels) < 2 { return errTooFewLabels } for _, label := range labels { if len(label) < 1 { return errLabelTooShort } if len(label) > maxLabelLength { return errLabelTooLong } if !dnsLabelRegexp.MatchString(label) { return errInvalidDNSCharacter } if label[len(label)-1] == '-' { return errInvalidDNSCharacter } if punycodeRegexp.MatchString(label) { if features.Enabled(features.IDNASupport) { // We don't care about script usage, if a name is resolvable it was // registered with a higher power and they should be enforcing their // own policy. As long as it was properly encoded that is enough // for us. _, err := idna.ToUnicode(label) if err != nil { return errMalformedIDN } } else { return errIDNNotSupported } } } // Names must end in an ICANN TLD, but they must not be equal to an ICANN TLD. icannTLD, err := extractDomainIANASuffix(domain) if err != nil { return errNonPublic } if icannTLD == domain { return errICANNTLD } // Require no match against blacklist if err := pa.checkHostLists(domain); err != nil { return err } return nil }