diff --git a/src/mod/acme/acme.go b/src/mod/acme/acme.go index e38415a..334e9c0 100644 --- a/src/mod/acme/acme.go +++ b/src/mod/acme/acme.go @@ -432,18 +432,18 @@ func (a *ACMEHandler) HandleGetExpiredDomains(w http.ResponseWriter, r *http.Req // to renew the certificate, and sends a JSON response indicating the result of the renewal process. func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Request) { domainPara, err := utils.PostPara(r, "domains") - + //Clean each domain cleanedDomains := []string{} - if (domainPara != "") { + if domainPara != "" { for _, d := range strings.Split(domainPara, ",") { // Apply normalization on each domain - nd, err := NormalizeDomain(d) + nd, err := utils.NormalizeDomain(d) if err != nil { utils.SendErrorResponse(w, jsonEscape(err.Error())) return - } - cleanedDomains = append(cleanedDomains, nd) + } + cleanedDomains = append(cleanedDomains, nd) } } @@ -507,7 +507,6 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ dns = true } - // Default propagation timeout is 300 seconds propagationTimeout := 300 if dns { @@ -549,7 +548,6 @@ func (a *ACMEHandler) HandleRenewCertificate(w http.ResponseWriter, r *http.Requ a.Logf("Could not extract SANs from PEM, using domainPara only", err) } - // Extract DNS servers from the request var dnsServers []string dnsServersPara, err := utils.PostPara(r, "dnsServers") diff --git a/src/mod/acme/utils.go b/src/mod/acme/utils.go index 0a2c3e3..df05f2e 100644 --- a/src/mod/acme/utils.go +++ b/src/mod/acme/utils.go @@ -7,8 +7,6 @@ import ( "fmt" "os" "time" - "strings" - "unicode" ) // Get the issuer name from pem file @@ -42,8 +40,6 @@ func ExtractDomains(certBytes []byte) ([]string, error) { return []string{}, errors.New("decode cert bytes failed") } - - func ExtractIssuerName(certBytes []byte) (string, error) { // Parse the PEM block block, _ := pem.Decode(certBytes) @@ -73,9 +69,9 @@ func ExtractDomainsFromPEM(pemFilePath string) ([]string, error) { certBytes, err := os.ReadFile(pemFilePath) if err != nil { - return nil, err + return nil, err } - domains,err := ExtractDomains(certBytes) + domains, err := ExtractDomains(certBytes) if err != nil { return nil, err } @@ -116,48 +112,3 @@ func CertExpireSoon(certBytes []byte, numberOfDays int) bool { } return false } - - -// NormalizeDomain cleans and validates a domain string. -// - Trims spaces around the domain -// - Converts to lowercase -// - Removes trailing dot (FQDN canonicalization) -// - Checks that the domain conforms to standard rules: -// * Each label ≤ 63 characters -// * Only letters, digits, and hyphens -// * Labels do not start or end with a hyphen -// * Full domain ≤ 253 characters -// Returns an empty string if the domain is invalid. -func NormalizeDomain(d string) (string, error) { - d = strings.TrimSpace(d) - d = strings.ToLower(d) - d = strings.TrimSuffix(d, ".") - - if len(d) == 0 { - return "", errors.New("domain is empty") - } - if len(d) > 253 { - return "", errors.New("domain exceeds 253 characters") - } - - labels := strings.Split(d, ".") - for _, label := range labels { - if len(label) == 0 { - return "", errors.New("Domain '" + d + "' not valid: Empty label") - } - if len(label) > 63 { - return "", errors.New("Domain not valid: label exceeds 63 characters") - } - - for i, r := range label { - if !(unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-') { - return "", errors.New("Domain '" + d + "' not valid: Invalid character '" + string(r) + "' in label") - } - if (i == 0 || i == len(label)-1) && r == '-' { - return "", errors.New("Domain '" + d + "' not valid: label '"+ label +"' starts or ends with hyphen") - } - } - } - - return d, nil -} diff --git a/src/mod/utils/utils.go b/src/mod/utils/utils.go index 2fe1ffd..c99de72 100644 --- a/src/mod/utils/utils.go +++ b/src/mod/utils/utils.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" "time" + "unicode" ) /* @@ -199,4 +200,54 @@ func ValidateListeningAddress(address string) bool { } return true -} \ No newline at end of file +} + +// NormalizeDomain cleans and validates a domain string. +// - Trims spaces around the domain +// - Converts to lowercase +// - Removes trailing dot (FQDN canonicalization) +// - Checks that the domain conforms to standard rules: +// - Each label ≤ 63 characters +// - Only letters, digits, and hyphens +// - Labels do not start or end with a hyphen +// - Full domain ≤ 253 characters +// +// Returns an empty string if the domain is invalid. +func NormalizeDomain(d string) (string, error) { + d = strings.TrimSpace(d) + d = strings.ToLower(d) + d = strings.TrimSuffix(d, ".") + + if len(d) == 0 { + return "", errors.New("domain is empty") + } + if len(d) > 253 { + return "", errors.New("domain exceeds 253 characters") + } + + labels := strings.Split(d, ".") + for index, label := range labels { + if index == 0 { + if len(label) == 1 && label == "*" { + continue + } + } + if len(label) == 0 { + return "", errors.New("Domain '" + d + "' not valid: Empty label") + } + if len(label) > 63 { + return "", errors.New("Domain not valid: label exceeds 63 characters") + } + + for i, r := range label { + if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '-' { + return "", errors.New("Domain '" + d + "' not valid: Invalid character '" + string(r) + "' in label") + } + if (i == 0 || i == len(label)-1) && r == '-' { + return "", errors.New("Domain '" + d + "' not valid: label '" + label + "' starts or ends with hyphen") + } + } + } + + return d, nil +}