// This file is part of the happyDomain (R) project. // Copyright (c) 2020-2025 happyDomain // Authors: Pierre-Olivier Mercier, et al. // // This program is offered under a commercial and under the AGPL license. // For commercial licensing, contact us at . // // For AGPL licensing: // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package abstract import ( "fmt" "strings" "github.com/miekg/dns" "git.happydns.org/happyDomain/internal/helpers" "git.happydns.org/happyDomain/model" svc "git.happydns.org/happyDomain/internal/service" ) type RFC6186 struct { Records []*dns.SRV `json:"srv"` } func (s *RFC6186) GetNbResources() int { return len(s.Records) } func (s *RFC6186) GenComment() string { var b strings.Builder var submission, submissionS, pop3, pop3s, imap, imaps uint for _, record := range s.Records { domain := record.Hdr.Name if strings.HasPrefix(domain, "_submission._tcp") { submission += 1 } else if strings.HasPrefix(domain, "_submissions._tcp") { submissionS += 1 } else if strings.HasPrefix(domain, "_imap._tcp") { imap += 1 } else if strings.HasPrefix(domain, "_imaps._tcp") { imaps += 1 } else if strings.HasPrefix(domain, "_pop3._tcp") { pop3 += 1 } else if strings.HasPrefix(domain, "_pop3s._tcp") { pop3s += 1 } } if submission > 1 { fmt.Fprintf(&b, "%d submissions", submission) } else if submission > 0 { b.WriteString("Submission") } if submissionS > 0 { if b.Len() > 0 { b.WriteString(" + ") } if submissionS > 1 { fmt.Fprintf(&b, "%d secured submissions", submissionS) } else if submissionS > 0 { if b.Len() > 0 { b.WriteString("secured submission") } else { b.WriteString("Secured submission") } } } if imap > 0 { if b.Len() > 0 { b.WriteString(" + ") } if imap > 1 { fmt.Fprintf(&b, "%d IMAP", imap) } else if imap > 0 { b.WriteString("IMAP") } } if imaps > 0 { if b.Len() > 0 { b.WriteString(" + ") } if imaps > 1 { fmt.Fprintf(&b, "%d secured IMAP", imaps) } else if imaps > 0 { b.WriteString("secured IMAP") } } if pop3 > 0 { if b.Len() > 0 { b.WriteString(" + ") } if pop3 > 1 { fmt.Fprintf(&b, "%d POP3", pop3) } else if pop3 > 0 { b.WriteString("POP3") } } if pop3s > 0 { if b.Len() > 0 { b.WriteString(" + ") } if pop3s > 1 { fmt.Fprintf(&b, "%d secured POP3", pop3s) } else if pop3s > 0 { b.WriteString("secured POP3") } } return b.String() } func (s *RFC6186) GetRecords(domain string, ttl uint32, origin string) ([]happydns.Record, error) { rrs := make([]happydns.Record, len(s.Records)) for i, r := range s.Records { srv := *r srv.Target = helpers.DomainFQDN(srv.Target, origin) rrs[i] = &srv } return rrs, nil } func rfc6186_analyze(a *svc.Analyzer) error { emailDomains := map[string]*RFC6186{} for _, prefix := range []string{ "_submission._tcp.", "_submissions._tcp.", // RFC 8314 "_imap._tcp.", "_imaps._tcp.", "_pop3._tcp.", "_pop3s._tcp.", } { for _, record := range a.SearchRR(svc.AnalyzerRecordFilter{Prefix: prefix, Type: dns.TypeSRV}) { domain := strings.TrimPrefix(record.Header().Name, prefix) if _, ok := emailDomains[domain]; !ok { emailDomains[domain] = &RFC6186{} } if srv, ok := record.(*dns.SRV); ok { // Make record relative srv.Target = helpers.DomainRelative(srv.Target, a.GetOrigin()) emailDomains[domain].Records = append(emailDomains[domain].Records, helpers.RRRelativeSubdomain(srv, a.GetOrigin(), domain).(*dns.SRV)) if err := a.UseRR( record, domain, emailDomains[domain], ); err != nil { return err } } } } return nil } func init() { svc.RegisterService( func() happydns.ServiceBody { return &RFC6186{} }, rfc6186_analyze, happydns.ServiceInfos{ Name: "E-Mail Services Discovery", Description: "Make email clients aware of the domain configuration to send and receive emails. RFC 6186", Family: happydns.SERVICE_FAMILY_ABSTRACT, Categories: []string{ "email", }, Restrictions: happydns.ServiceRestrictions{ NearAlone: true, Single: true, NeedTypes: []uint16{ dns.TypeSRV, }, }, }, 2, ) }