90 lines
2.6 KiB
Go
90 lines
2.6 KiB
Go
// Package checker detects dangling pointer records (CNAME/MX/SRV/NS) whose external targets
|
|
// may have expired or been re-registered, enabling subdomain takeover.
|
|
package checker
|
|
|
|
import (
|
|
"encoding/json"
|
|
)
|
|
|
|
const ObservationKeyDangling = "dangling_records"
|
|
|
|
// DanglingData is the raw observation payload; one Pointer per (owner, rrtype, target) triple.
|
|
type DanglingData struct {
|
|
Zone string `json:"zone,omitempty"`
|
|
ServicesScanned int `json:"services_scanned"`
|
|
Pointers []Pointer `json:"pointers,omitempty"`
|
|
// CollectErrors surfaces non-fatal scan problems so silent skips don't masquerade as a clean pass.
|
|
CollectErrors []string `json:"collect_errors,omitempty"`
|
|
}
|
|
|
|
// Pointer is one (owner, rrtype, target) triple from the zone, with its DNS resolution verdict.
|
|
type Pointer struct {
|
|
Owner string `json:"owner"`
|
|
Subdomain string `json:"subdomain"`
|
|
Rrtype string `json:"rrtype"`
|
|
Target string `json:"target"`
|
|
// External flags takeover risk: Target's registrable domain differs from the zone's.
|
|
External bool `json:"external"`
|
|
Registrable string `json:"registrable,omitempty"`
|
|
// ServiceType identifies the happyDomain service for linking back to the edit screen.
|
|
ServiceType string `json:"service_type,omitempty"`
|
|
Resolution string `json:"resolution"`
|
|
ResolutionDetail string `json:"resolution_detail,omitempty"`
|
|
}
|
|
|
|
// rawZone is a partial Zone type redeclared here to avoid importing the happyDomain module.
|
|
type rawZone struct {
|
|
DomainName string `json:"domain_name,omitempty"`
|
|
Services map[string][]rawService `json:"services"`
|
|
}
|
|
|
|
type rawService struct {
|
|
Type string `json:"_svctype"`
|
|
Domain string `json:"_domain"`
|
|
Service json.RawMessage `json:"Service"`
|
|
}
|
|
|
|
type cnameBody struct {
|
|
Record struct {
|
|
Hdr struct {
|
|
Name string `json:"Name"`
|
|
} `json:"Hdr"`
|
|
Target string `json:"Target"`
|
|
} `json:"cname"`
|
|
}
|
|
|
|
type mxRecord struct {
|
|
Hdr struct {
|
|
Name string `json:"Name"`
|
|
} `json:"Hdr"`
|
|
Mx string `json:"Mx"`
|
|
}
|
|
|
|
type mxsBody struct {
|
|
MXs []mxRecord `json:"mx"`
|
|
}
|
|
|
|
type srvRecord struct {
|
|
Hdr struct {
|
|
Name string `json:"Name"`
|
|
} `json:"Hdr"`
|
|
Target string `json:"Target"`
|
|
}
|
|
|
|
type srvsBody struct {
|
|
Records []srvRecord `json:"srv"`
|
|
}
|
|
|
|
// orphanRecord wraps an svcs.Orphan body; Hdr.Rrtype is sniffed to pick the right field.
|
|
type orphanRecord struct {
|
|
Record struct {
|
|
Hdr struct {
|
|
Name string `json:"Name"`
|
|
Rrtype uint16 `json:"Rrtype"`
|
|
} `json:"Hdr"`
|
|
// Optional fields, populated for the relevant rrtype.
|
|
Target string `json:"Target,omitempty"`
|
|
Mx string `json:"Mx,omitempty"`
|
|
Ns string `json:"Ns,omitempty"`
|
|
} `json:"record"`
|
|
}
|