Codebase list certgraph / run/d960c48e-a500-4dea-b2c1-f3dce5bee045/upstream graph / domain_node.go
run/d960c48e-a500-4dea-b2c1-f3dce5bee045/upstream

Tree @run/d960c48e-a500-4dea-b2c1-f3dce5bee045/upstream (Download .tar.gz)

domain_node.go @run/d960c48e-a500-4dea-b2c1-f3dce5bee045/upstreamraw · history · blame

package graph

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/lanrat/certgraph/dns"
	"github.com/lanrat/certgraph/fingerprint"
	"github.com/lanrat/certgraph/status"
)

// DomainNode structure to store a domain and its edges
type DomainNode struct {
	Domain         string
	Depth          uint
	Certs          map[fingerprint.Fingerprint][]string
	RelatedDomains status.Map
	Status         status.Status
	Root           bool
	HasDNS         bool
}

// NewDomainNode constructor for DomainNode, converts domain to lower nonWildcard
func NewDomainNode(domain string, depth uint) *DomainNode {
	domainNode := new(DomainNode)
	domainNode.Domain = nonWildcard(strings.ToLower(domain))
	domainNode.Depth = depth
	domainNode.Certs = make(map[fingerprint.Fingerprint][]string)
	domainNode.RelatedDomains = make(status.Map)
	return domainNode
}

// AddRelatedDomains adds the domains in the provided array to the domainNode's
// related domains status map with an unknown status if they are not already
// in the map
func (d *DomainNode) AddRelatedDomains(domains []string) {
	for _, domain := range domains {
		domain = strings.ToLower(domain)
		if _, ok := d.RelatedDomains[domain]; ok {
			continue
		}
		d.RelatedDomains[domain] = status.New(status.UNKNOWN)
	}
}

// CheckForDNS checks for the existence of DNS records for the domain's apex
// sets the value to the node and returns the result as well
func (d *DomainNode) CheckForDNS(timeout time.Duration) (bool, error) {
	hasDNS, err := dns.HasRecordsCache(d.Domain, timeout)

	d.HasDNS = hasDNS
	return hasDNS, err
}

// AddStatusMap adds the status' in the map to the DomainNode
// also sets the Node's own status if it is in the Map
// side effect: will delete its own status from the provided map
func (d *DomainNode) AddStatusMap(m status.Map) {
	if status, ok := m[d.Domain]; ok {
		d.Status = status
		delete(m, d.Domain)
	}
	for domain, status := range m {
		d.RelatedDomains[domain] = status
	}
}

// GetCertificates returns a list of known certificate fingerprints for the domain
func (d *DomainNode) GetCertificates() []fingerprint.Fingerprint {
	fingerprints := make([]fingerprint.Fingerprint, 0, len(d.Certs))
	for fingerprint := range d.Certs {
		fingerprints = append(fingerprints, fingerprint)
	}
	return fingerprints
}

// String returns the string representation of a node
func (d *DomainNode) String() string {
	certString := ""
	// Certs
	if len(d.Certs) > 0 {
		for fingerprint := range d.Certs {
			certString = fmt.Sprintf("%s %s", certString, fingerprint.HexString())
		}
	}
	return fmt.Sprintf("%s\t%d\t%s\t%s", d.Domain, d.Depth, d.Status.String(), certString)
}

// AddCertFingerprint appends a Fingerprint to the DomainNode's list of certificates
func (d *DomainNode) AddCertFingerprint(fp fingerprint.Fingerprint, certSource string) {
	d.Certs[fp] = append(d.Certs[fp], certSource)
}

// ToMap returns a map of the DomainNode's fields (weak serialization)
func (d *DomainNode) ToMap() map[string]string {
	related := make([]string, 0, len(d.RelatedDomains))
	for domain := range d.RelatedDomains {
		related = append(related, domain)
	}
	relatedString := strings.Join(related, " ")
	m := make(map[string]string)
	m["type"] = "domain"
	m["id"] = d.Domain
	m["status"] = d.Status.String()
	m["root"] = strconv.FormatBool(d.Root)
	m["depth"] = strconv.FormatUint(uint64(d.Depth), 10)
	m["related"] = relatedString
	m["hasDNS"] = strconv.FormatBool(d.HasDNS)
	return m
}