Compare commits
6 commits
7b01b5f99b
...
2772e6905e
| Author | SHA1 | Date | |
|---|---|---|---|
| 2772e6905e | |||
| a2c6040974 | |||
| 330c675da5 | |||
| effb588d3c | |||
| e73ed40d5c | |||
| 99edd8a66c |
11 changed files with 274 additions and 49 deletions
|
|
@ -98,7 +98,7 @@ func (dc *DomainController) AddDomain(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
var uz happydns.Domain
|
||||
var uz happydns.DomainCreationInput
|
||||
err := c.ShouldBindJSON(&uz)
|
||||
if err != nil {
|
||||
log.Printf("%s sends invalid Domain JSON: %s", c.ClientIP(), err.Error())
|
||||
|
|
@ -106,13 +106,13 @@ func (dc *DomainController) AddDomain(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
err = dc.domainService.CreateDomain(c.Request.Context(), user, &uz)
|
||||
domain, err := dc.domainService.CreateDomain(c.Request.Context(), user, &uz)
|
||||
if err != nil {
|
||||
middleware.ErrorResponse(c, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, uz)
|
||||
c.JSON(http.StatusOK, domain)
|
||||
}
|
||||
|
||||
// GetDomain retrieves information about a given Domain owned by the user.
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ func (p *recordPool) SearchRR(arrs ...AnalyzerRecordFilter) (rrs []happydns.Reco
|
|||
rdtype := rhdr.Rrtype
|
||||
if strings.HasPrefix(rhdr.Name, arr.Prefix) &&
|
||||
strings.HasSuffix(rhdr.Name, arr.SubdomainsOf) &&
|
||||
strings.Contains(rhdr.Name, arr.DomainContains) &&
|
||||
(arr.Domain == "" || rhdr.Name == arr.Domain || rhdr.Name == strings.TrimSuffix(arr.Domain, ".")) &&
|
||||
(arr.Type == 0 || rdtype == arr.Type) &&
|
||||
(arr.Ttl == 0 || rhdr.Ttl == arr.Ttl) &&
|
||||
|
|
@ -133,12 +134,13 @@ func (sa *serviceAccumulator) addService(rr happydns.Record, domain string, svc
|
|||
// AnalyzerRecordFilter specifies criteria for matching DNS records.
|
||||
// Zero-value fields are treated as wildcards (match anything).
|
||||
type AnalyzerRecordFilter struct {
|
||||
Prefix string
|
||||
Domain string
|
||||
SubdomainsOf string
|
||||
Contains string
|
||||
Type uint16
|
||||
Ttl uint32
|
||||
Prefix string
|
||||
Domain string
|
||||
SubdomainsOf string
|
||||
DomainContains string
|
||||
Contains string
|
||||
Type uint16
|
||||
Ttl uint32
|
||||
}
|
||||
|
||||
// Analyzer holds the state for zone analysis. It is composed of a recordPool
|
||||
|
|
|
|||
|
|
@ -66,23 +66,23 @@ func NewService(
|
|||
}
|
||||
|
||||
// CreateDomain creates a new domain for the given user.
|
||||
func (s *Service) CreateDomain(ctx context.Context, user *happydns.User, uz *happydns.Domain) error {
|
||||
uz, err := happydns.NewDomain(user, uz.DomainName, uz.ProviderId)
|
||||
func (s *Service) CreateDomain(ctx context.Context, user *happydns.User, input *happydns.DomainCreationInput) (*happydns.Domain, error) {
|
||||
uz, err := happydns.NewDomain(user, input.DomainName, input.ProviderId)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
provider, err := s.providerService.GetUserProvider(ctx, user, uz.ProviderId)
|
||||
if err != nil {
|
||||
return happydns.ValidationError{Msg: fmt.Sprintf("unable to find the provider.")}
|
||||
return nil, happydns.ValidationError{Msg: fmt.Sprintf("unable to find the provider.")}
|
||||
}
|
||||
|
||||
if err = s.domainExistence.TestDomainExistence(ctx, provider, uz.DomainName); err != nil {
|
||||
return happydns.NotFoundError{Msg: err.Error()}
|
||||
return nil, happydns.NotFoundError{Msg: err.Error()}
|
||||
}
|
||||
|
||||
if err := s.store.CreateDomain(uz); err != nil {
|
||||
return happydns.InternalError{
|
||||
return nil, happydns.InternalError{
|
||||
Err: fmt.Errorf("unable to CreateDomain: %s", err),
|
||||
UserMessage: "Sorry, we are unable to create your domain now.",
|
||||
}
|
||||
|
|
@ -93,7 +93,7 @@ func (s *Service) CreateDomain(ctx context.Context, user *happydns.User, uz *hap
|
|||
s.domainLogAppender.AppendDomainLog(uz, happydns.NewDomainLog(user, happydns.LOG_INFO, fmt.Sprintf("Domain name %s added.", uz.DomainName)))
|
||||
}
|
||||
|
||||
return nil
|
||||
return uz, nil
|
||||
}
|
||||
|
||||
// GetUserDomain retrieves a domain by ID for the given user.
|
||||
|
|
|
|||
|
|
@ -155,12 +155,12 @@ func Test_CreateDomain(t *testing.T) {
|
|||
user := createTestUser(t, db, "test@example.com")
|
||||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "example.com",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
|
@ -200,12 +200,12 @@ func Test_CreateDomain_InvalidProvider(t *testing.T) {
|
|||
user := createTestUser(t, db, "test@example.com")
|
||||
invalidProviderId := happydns.Identifier([]byte("invalid-provider"))
|
||||
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "example.com",
|
||||
ProviderId: invalidProviderId,
|
||||
}
|
||||
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err == nil {
|
||||
t.Error("expected error when creating domain with invalid provider")
|
||||
}
|
||||
|
|
@ -219,11 +219,11 @@ func Test_GetUserDomain(t *testing.T) {
|
|||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "example.com",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
@ -259,11 +259,11 @@ func Test_GetUserDomain_WrongUser(t *testing.T) {
|
|||
providerId := createTestProvider(t, db, user1, "Test Provider")
|
||||
|
||||
// Create a domain for user1
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "user1-domain.com",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user1, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user1, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
@ -309,11 +309,11 @@ func Test_GetUserDomainByFQDN(t *testing.T) {
|
|||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "example.com.",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
@ -343,11 +343,11 @@ func Test_ListUserDomains(t *testing.T) {
|
|||
// Create multiple domains
|
||||
domainNames := []string{"example1.com", "example2.com", "example3.com"}
|
||||
for _, name := range domainNames {
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: name,
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain %s: %v", name, err)
|
||||
}
|
||||
|
|
@ -375,22 +375,22 @@ func Test_ListUserDomains_MultipleUsers(t *testing.T) {
|
|||
|
||||
// Create domains for user1
|
||||
for i := 1; i <= 2; i++ {
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: fmt.Sprintf("user1-domain%d.com", i),
|
||||
ProviderId: providerId1,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user1, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user1, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create domain for user2
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "user2-domain.com",
|
||||
ProviderId: providerId2,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user2, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user2, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
@ -439,11 +439,11 @@ func Test_UpdateDomain(t *testing.T) {
|
|||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "example.com",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
@ -490,11 +490,11 @@ func Test_UpdateDomain_PreventIdChange(t *testing.T) {
|
|||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "example.com",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
@ -532,11 +532,11 @@ func Test_UpdateDomain_WrongUser(t *testing.T) {
|
|||
providerId := createTestProvider(t, db, user1, "Test Provider")
|
||||
|
||||
// Create a domain for user1
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "user1-domain.com",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user1, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user1, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
@ -565,11 +565,11 @@ func Test_DeleteDomain(t *testing.T) {
|
|||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "example.com",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
@ -605,11 +605,11 @@ func Test_UpdateDomain_Alias(t *testing.T) {
|
|||
providerId := createTestProvider(t, db, user, "Test Provider")
|
||||
|
||||
// Create a domain
|
||||
domainToCreate := &happydns.Domain{
|
||||
domainToCreate := &happydns.DomainCreationInput{
|
||||
DomainName: "example.com",
|
||||
ProviderId: providerId,
|
||||
}
|
||||
err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
_, err := service.CreateDomain(ctx, user, domainToCreate)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create domain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ type Subdomain string
|
|||
type Origin string
|
||||
|
||||
type DomainUsecase interface {
|
||||
CreateDomain(context.Context, *User, *Domain) error
|
||||
CreateDomain(context.Context, *User, *DomainCreationInput) (*Domain, error)
|
||||
DeleteDomain(Identifier) error
|
||||
ExtendsDomainWithZoneMeta(*Domain) (*DomainWithZoneMetadata, error)
|
||||
GetUserDomain(*User, Identifier) (*Domain, error)
|
||||
|
|
|
|||
|
|
@ -93,10 +93,10 @@ type ServiceMeta struct {
|
|||
OwnerId Identifier `json:"_ownerid,omitempty" swaggertype:"string"`
|
||||
|
||||
// Domain contains the abstract domain where this Service relates.
|
||||
Domain string `json:"_domain" binding:"required"`
|
||||
Domain string `json:"_domain" validate:"required"`
|
||||
|
||||
// Ttl contains the specific TTL for the underlying Resources.
|
||||
Ttl uint32 `json:"_ttl" binding:"required"`
|
||||
Ttl uint32 `json:"_ttl" validate:"required"`
|
||||
|
||||
// Comment is a string that helps user to distinguish the Service.
|
||||
Comment string `json:"_comment,omitempty"`
|
||||
|
|
@ -109,7 +109,7 @@ type ServiceMeta struct {
|
|||
Aliases []string `json:"_aliases,omitempty"`
|
||||
|
||||
// NbResources holds the number of Resources stored inside this Service.
|
||||
NbResources int `json:"_tmp_hint_nb" binding:"required"`
|
||||
NbResources int `json:"_tmp_hint_nb" validate:"required"`
|
||||
|
||||
// PropagatedAt is the estimated time at which the last published changes
|
||||
// for this service will be fully propagated (old cached records expired).
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ type ZoneMeta struct {
|
|||
ParentZone *Identifier `json:"parent,omitempty" swaggertype:"string"`
|
||||
|
||||
// DefaultTTL is the TTL to use when no TTL has been defined for a record in this Zone.
|
||||
DefaultTTL uint32 `json:"default_ttl" binding:"required"`
|
||||
DefaultTTL uint32 `json:"default_ttl" validate:"required"`
|
||||
|
||||
// LastModified holds the time when the last modification has been made on this Zone.
|
||||
LastModified time.Time `json:"last_modified" format:"date-time" binding:"required"`
|
||||
|
|
|
|||
112
services/abstract/libravatar.go
Normal file
112
services/abstract/libravatar.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// 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 <contact@happydomain.org>.
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package abstract
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/helpers"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
"git.happydns.org/happyDomain/services"
|
||||
)
|
||||
|
||||
type LibravatarServer struct {
|
||||
Records []*dns.SRV `json:"srv"`
|
||||
}
|
||||
|
||||
func (s *LibravatarServer) GetNbResources() int {
|
||||
return len(s.Records)
|
||||
}
|
||||
|
||||
func (s *LibravatarServer) GenComment() string {
|
||||
if len(s.Records) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return s.Records[0].Target
|
||||
}
|
||||
|
||||
func (s *LibravatarServer) 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 libavatar_analyze(a *svcs.Analyzer) error {
|
||||
alreadyUsed := map[string]*LibravatarServer{}
|
||||
|
||||
for _, record := range a.SearchRR(svcs.AnalyzerRecordFilter{Type: dns.TypeSRV, Prefix: "_avatars"}) {
|
||||
domain := ""
|
||||
if strings.HasPrefix(record.Header().Name, "_avatars._tcp.") {
|
||||
domain = strings.TrimPrefix(record.Header().Name, "_avatars._tcp.")
|
||||
} else if strings.HasPrefix(record.Header().Name, "_avatars-sec._tcp.") {
|
||||
domain = strings.TrimPrefix(record.Header().Name, "_avatars-sec._tcp.")
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if srv, ok := record.(*dns.SRV); ok {
|
||||
var rr *LibravatarServer
|
||||
|
||||
// Make record relative
|
||||
srv.Target = helpers.DomainRelative(srv.Target, a.GetOrigin())
|
||||
|
||||
if ls, ok := alreadyUsed[srv.Target]; ok {
|
||||
rr = ls
|
||||
rr.Records = append(rr.Records, helpers.RRRelative(srv, a.GetOrigin()).(*dns.SRV))
|
||||
} else {
|
||||
rr = &LibravatarServer{
|
||||
Records: []*dns.SRV{helpers.RRRelative(srv, a.GetOrigin()).(*dns.SRV)},
|
||||
}
|
||||
|
||||
alreadyUsed[srv.Target] = rr
|
||||
}
|
||||
|
||||
a.UseRR(record, domain, rr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
svcs.RegisterService(
|
||||
func() happydns.ServiceBody {
|
||||
return &LibravatarServer{}
|
||||
},
|
||||
libavatar_analyze,
|
||||
happydns.ServiceInfos{
|
||||
Name: "Federated Avatar",
|
||||
Description: "Declare a libravatar server for this subdomain.",
|
||||
Family: happydns.SERVICE_FAMILY_ABSTRACT,
|
||||
Categories: []string{
|
||||
"service",
|
||||
},
|
||||
},
|
||||
2,
|
||||
)
|
||||
}
|
||||
|
|
@ -164,7 +164,7 @@ func (s *DKIMRedirection) GetRecords(domain string, ttl uint32, origin string) (
|
|||
}
|
||||
|
||||
func dkim_analyze(a *svc.Analyzer) (err error) {
|
||||
for _, record := range a.SearchRR(svc.AnalyzerRecordFilter{Type: dns.TypeTXT}) {
|
||||
for _, record := range a.SearchRR(svc.AnalyzerRecordFilter{Type: dns.TypeTXT, DomainContains: "._domainkey."}) {
|
||||
dkidx := strings.Index(record.Header().Name, "._domainkey.")
|
||||
if dkidx <= 0 {
|
||||
continue
|
||||
|
|
|
|||
111
services/dmarc-report.go
Normal file
111
services/dmarc-report.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// 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 <contact@happydomain.org>.
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
package svcs
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"git.happydns.org/happyDomain/internal/helpers"
|
||||
"git.happydns.org/happyDomain/model"
|
||||
)
|
||||
|
||||
type DMARCReport struct {
|
||||
Records []*happydns.TXT `json:"txt"`
|
||||
}
|
||||
|
||||
func (s *DMARCReport) GetNbResources() int {
|
||||
return len(s.Records)
|
||||
}
|
||||
|
||||
func (s *DMARCReport) GenComment() string {
|
||||
var domains []string
|
||||
|
||||
for _, rr := range s.Records {
|
||||
domains = append(domains, strings.TrimSuffix(rr.Header().Name, "._report._dmarc"))
|
||||
}
|
||||
|
||||
return strings.Join(domains, ", ")
|
||||
}
|
||||
|
||||
func (t *DMARCReport) GetRecords(domain string, ttl uint32, origin string) (rrs []happydns.Record, e error) {
|
||||
for _, rr := range t.Records {
|
||||
rrs = append(rrs, rr)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
func dmarc_report_analyze(a *Analyzer) (err error) {
|
||||
services := map[string]*DMARCReport{}
|
||||
|
||||
for _, record := range a.SearchRR(AnalyzerRecordFilter{Type: dns.TypeTXT, DomainContains: "._report._dmarc"}) {
|
||||
txt, ok := record.(*happydns.TXT)
|
||||
dmidx := strings.Index(record.Header().Name, "._report._dmarc.")
|
||||
if dmidx <= 0 || !ok || !strings.HasPrefix(strings.ToLower(txt.Txt), "v=dmarc1") {
|
||||
continue
|
||||
}
|
||||
domain := record.Header().Name[dmidx+16:]
|
||||
|
||||
if _, ok := services[domain]; !ok {
|
||||
services[domain] = &DMARCReport{}
|
||||
}
|
||||
|
||||
services[domain].Records = append(
|
||||
services[domain].Records,
|
||||
helpers.RRRelative(record, domain).(*happydns.TXT),
|
||||
)
|
||||
|
||||
err = a.UseRR(record, domain, services[domain])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterService(
|
||||
func() happydns.ServiceBody {
|
||||
return &DMARCReport{}
|
||||
},
|
||||
dmarc_report_analyze,
|
||||
happydns.ServiceInfos{
|
||||
Name: "DMARC allow receiving reports",
|
||||
Description: "Allow a domain to receive DMARC reports for another domain.",
|
||||
Categories: []string{
|
||||
"email",
|
||||
},
|
||||
RecordTypes: []uint16{
|
||||
dns.TypeTXT,
|
||||
},
|
||||
Restrictions: happydns.ServiceRestrictions{
|
||||
NearAlone: true,
|
||||
NeedTypes: []uint16{
|
||||
dns.TypeTXT,
|
||||
},
|
||||
},
|
||||
},
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
|
@ -149,7 +149,7 @@
|
|||
<RecordLine
|
||||
dn={service._domain || ""}
|
||||
origin={domain}
|
||||
bind:rr={rrs[i]}
|
||||
rr={rrs[i]}
|
||||
onopen={() => (isOpen = false)}
|
||||
/>
|
||||
{/each}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue