2023-12-24 10:18:08 +00:00
|
|
|
// This file is part of the happyDomain (R) project.
|
|
|
|
// Copyright (c) 2020-2024 happyDomain
|
|
|
|
// Authors: Pierre-Olivier Mercier, et al.
|
2020-05-09 11:14:29 +00:00
|
|
|
//
|
2023-12-24 10:18:08 +00:00
|
|
|
// This program is offered under a commercial and under the AGPL license.
|
|
|
|
// For commercial licensing, contact us at <contact@happydomain.org>.
|
2020-05-09 11:14:29 +00:00
|
|
|
//
|
2023-12-24 10:18:08 +00:00
|
|
|
// 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.
|
2020-05-09 11:14:29 +00:00
|
|
|
//
|
2023-12-24 10:18:08 +00:00
|
|
|
// 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.
|
2020-05-09 11:14:29 +00:00
|
|
|
//
|
2023-12-24 10:18:08 +00:00
|
|
|
// 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/>.
|
2020-05-09 11:14:29 +00:00
|
|
|
|
|
|
|
package svcs
|
|
|
|
|
|
|
|
import (
|
2020-06-08 23:46:02 +00:00
|
|
|
"crypto/sha1"
|
2020-05-09 11:14:29 +00:00
|
|
|
"errors"
|
2023-09-15 18:25:31 +00:00
|
|
|
"fmt"
|
2020-06-08 23:46:02 +00:00
|
|
|
"io"
|
2020-05-09 11:14:29 +00:00
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
|
2023-09-15 18:25:31 +00:00
|
|
|
"github.com/StackExchange/dnscontrol/v4/models"
|
2020-05-09 11:14:29 +00:00
|
|
|
"github.com/miekg/dns"
|
|
|
|
|
2023-09-07 09:37:18 +00:00
|
|
|
"git.happydns.org/happyDomain/model"
|
|
|
|
"git.happydns.org/happyDomain/utils"
|
2020-05-09 11:14:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Analyzer struct {
|
2020-06-08 14:34:28 +00:00
|
|
|
origin string
|
2023-09-15 18:25:31 +00:00
|
|
|
zone models.Records
|
2020-06-08 14:34:28 +00:00
|
|
|
services map[string][]*happydns.ServiceCombined
|
|
|
|
defaultTTL uint32
|
2020-05-09 11:14:29 +00:00
|
|
|
}
|
|
|
|
|
2021-01-06 02:20:31 +00:00
|
|
|
func (a *Analyzer) GetOrigin() string {
|
|
|
|
return a.origin
|
|
|
|
}
|
|
|
|
|
2020-05-09 11:14:29 +00:00
|
|
|
type AnalyzerRecordFilter struct {
|
|
|
|
Prefix string
|
|
|
|
Domain string
|
|
|
|
SubdomainsOf string
|
|
|
|
Contains string
|
|
|
|
Type uint16
|
|
|
|
Ttl uint32
|
|
|
|
}
|
|
|
|
|
2023-09-15 18:25:31 +00:00
|
|
|
func (a *Analyzer) SearchRR(arrs ...AnalyzerRecordFilter) (rrs models.Records) {
|
2020-05-09 11:14:29 +00:00
|
|
|
for _, record := range a.zone {
|
|
|
|
for _, arr := range arrs {
|
2023-09-15 18:25:31 +00:00
|
|
|
if rdtype, ok := dns.StringToType[record.Type]; strings.HasPrefix(record.NameFQDN, arr.Prefix) &&
|
|
|
|
strings.HasSuffix(record.NameFQDN, arr.SubdomainsOf) &&
|
2023-12-06 00:53:16 +00:00
|
|
|
(arr.Domain == "" || record.NameFQDN == strings.TrimSuffix(arr.Domain, ".")) &&
|
2023-09-15 18:25:31 +00:00
|
|
|
(arr.Type == 0 || (ok && rdtype == arr.Type)) &&
|
|
|
|
(arr.Ttl == 0 || record.TTL == arr.Ttl) &&
|
|
|
|
(arr.Contains == "" || strings.Contains(fmt.Sprintf("%s. %d IN %s %s", record.NameFQDN, record.TTL, record.Type, record.String()), arr.Contains)) {
|
2020-05-09 11:14:29 +00:00
|
|
|
rrs = append(rrs, record)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-09-15 18:25:31 +00:00
|
|
|
func (a *Analyzer) UseRR(rr *models.RecordConfig, domain string, svc happydns.Service) error {
|
2020-05-09 11:14:29 +00:00
|
|
|
found := false
|
|
|
|
for k, record := range a.zone {
|
|
|
|
if record == rr {
|
|
|
|
found = true
|
|
|
|
a.zone[k] = a.zone[len(a.zone)-1]
|
|
|
|
a.zone = a.zone[:len(a.zone)-1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !found {
|
|
|
|
return errors.New("Record not found.")
|
|
|
|
}
|
|
|
|
|
2020-05-10 10:27:03 +00:00
|
|
|
// svc nil, just drop the record from the zone (probably handle another way)
|
|
|
|
if svc == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-14 10:56:06 +00:00
|
|
|
// Remove origin to get an relative domain here
|
2023-09-15 18:25:31 +00:00
|
|
|
domain = strings.TrimSuffix(strings.TrimSuffix(strings.TrimSuffix(domain, "."), strings.TrimSuffix(a.origin, ".")), ".")
|
2020-06-14 10:56:06 +00:00
|
|
|
|
2020-05-09 11:14:29 +00:00
|
|
|
for _, service := range a.services[domain] {
|
|
|
|
if service.Service == svc {
|
2020-05-10 09:23:55 +00:00
|
|
|
service.Comment = svc.GenComment(a.origin)
|
|
|
|
service.NbResources = svc.GetNbResources()
|
2020-05-09 11:14:29 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 23:46:02 +00:00
|
|
|
hash := sha1.New()
|
|
|
|
io.WriteString(hash, rr.String())
|
|
|
|
|
2020-06-08 14:34:28 +00:00
|
|
|
var ttl uint32 = 0
|
2023-09-15 18:25:31 +00:00
|
|
|
if rr.TTL != a.defaultTTL {
|
|
|
|
ttl = rr.TTL
|
2020-06-08 14:34:28 +00:00
|
|
|
}
|
|
|
|
|
2023-05-20 10:43:32 +00:00
|
|
|
a.services[domain] = append(a.services[domain], &happydns.ServiceCombined{
|
|
|
|
Service: svc,
|
|
|
|
ServiceMeta: happydns.ServiceMeta{
|
|
|
|
Id: hash.Sum(nil),
|
|
|
|
Type: reflect.Indirect(reflect.ValueOf(svc)).Type().String(),
|
|
|
|
Domain: domain,
|
|
|
|
Ttl: ttl,
|
|
|
|
Comment: svc.GenComment(a.origin),
|
|
|
|
NbResources: svc.GetNbResources(),
|
|
|
|
},
|
|
|
|
})
|
2020-05-09 11:14:29 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-09-15 18:25:31 +00:00
|
|
|
func getMostUsedTTL(zone models.Records) uint32 {
|
2020-06-08 14:34:28 +00:00
|
|
|
ttls := map[uint32]int{}
|
|
|
|
for _, rr := range zone {
|
2023-09-15 18:25:31 +00:00
|
|
|
ttls[rr.TTL] += 1
|
2020-06-08 14:34:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var max uint32 = 0
|
|
|
|
for k, v := range ttls {
|
|
|
|
if w, ok := ttls[max]; !ok || v > w {
|
|
|
|
max = k
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return max
|
|
|
|
}
|
|
|
|
|
2023-09-15 18:25:31 +00:00
|
|
|
func AnalyzeZone(origin string, zone models.Records) (svcs map[string][]*happydns.ServiceCombined, defaultTTL uint32, err error) {
|
2020-06-08 14:34:28 +00:00
|
|
|
defaultTTL = getMostUsedTTL(zone)
|
|
|
|
|
2020-05-09 11:14:29 +00:00
|
|
|
a := Analyzer{
|
2020-06-08 14:34:28 +00:00
|
|
|
origin: origin,
|
|
|
|
zone: zone,
|
|
|
|
services: map[string][]*happydns.ServiceCombined{},
|
|
|
|
defaultTTL: defaultTTL,
|
2020-05-09 11:14:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find services between all registered ones
|
|
|
|
for _, service := range OrderedServices() {
|
|
|
|
if service.Analyzer == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = service.Analyzer(&a); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
svcs = a.services
|
|
|
|
|
|
|
|
// Consider records not used by services as Orphan
|
|
|
|
for _, record := range a.zone {
|
|
|
|
// Skip DNSSEC records
|
2023-09-15 18:25:31 +00:00
|
|
|
if rdtype, ok := dns.StringToType[record.Type]; ok && utils.IsDNSSECType(rdtype) {
|
2020-05-09 11:14:29 +00:00
|
|
|
continue
|
|
|
|
}
|
2023-09-15 18:25:31 +00:00
|
|
|
if record.NameFQDN == "__dnssec."+origin && record.Type == "TXT" {
|
2021-07-05 09:32:04 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-05-09 11:14:29 +00:00
|
|
|
|
2023-09-15 18:25:31 +00:00
|
|
|
domain := strings.TrimSuffix(strings.TrimSuffix(strings.TrimSuffix(record.NameFQDN, "."), strings.TrimSuffix(a.origin, ".")), ".")
|
2020-06-08 23:46:02 +00:00
|
|
|
|
|
|
|
hash := sha1.New()
|
|
|
|
io.WriteString(hash, record.String())
|
|
|
|
|
2023-09-15 18:25:31 +00:00
|
|
|
orphan := &Orphan{record.Type, record.String()}
|
2023-05-20 10:43:32 +00:00
|
|
|
svcs[domain] = append(svcs[domain], &happydns.ServiceCombined{
|
|
|
|
Service: orphan,
|
|
|
|
ServiceMeta: happydns.ServiceMeta{
|
|
|
|
Id: hash.Sum(nil),
|
|
|
|
Type: reflect.Indirect(reflect.ValueOf(orphan)).Type().String(),
|
|
|
|
Domain: domain,
|
2023-09-15 18:25:31 +00:00
|
|
|
Ttl: record.TTL,
|
2023-05-20 10:43:32 +00:00
|
|
|
NbResources: 1,
|
|
|
|
Comment: orphan.GenComment(a.origin),
|
|
|
|
},
|
|
|
|
})
|
2020-05-09 11:14:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|