Skip to content

Commit d066e5e

Browse files
committed
multi driver status reporting
1 parent be7c549 commit d066e5e

2 files changed

Lines changed: 119 additions & 38 deletions

File tree

certgraph.go

Lines changed: 87 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ import (
3636
)
3737

3838
var (
39-
version = "dev"
40-
certGraph = graph.NewCertGraph()
41-
processedCerts = make(map[fingerprint.Fingerprint]bool) // Session-wide cache for processed certificates
39+
version = "dev"
40+
certGraph = graph.NewCertGraph()
41+
processedCerts = make(map[fingerprint.Fingerprint]bool) // Session-wide cache for processed certificates
42+
processedCertsMutex sync.Mutex // Protects processedCerts map
4243
)
4344

4445
// temp flag vars
@@ -408,7 +409,6 @@ func visit(domainNode *graph.DomainNode) {
408409
}
409410
domainNode.AddRelatedDomains(relatedDomains)
410411

411-
// TODO parallelize this
412412
// TODO fix printing domains as they are found with new driver
413413
// add cert nodes to graph
414414
fingerprintMap, err := results.GetFingerprints()
@@ -419,32 +419,88 @@ func visit(domainNode *graph.DomainNode) {
419419

420420
// fingerprints for the domain queried
421421
fingerprints := fingerprintMap[domainNode.Domain]
422-
for _, fp := range fingerprints {
423-
// add certNode to graph
424-
certNode, exists := certGraph.GetCert(fp)
425-
if !exists {
426-
// Check if we've already attempted to process this certificate
427-
if processedCerts[fp] {
428-
v("Certificate processing already attempted, skipping:", fp.HexString())
429-
continue
430-
}
422+
423+
// Parallelize certificate processing using worker pool
424+
type certWork struct {
425+
fp fingerprint.Fingerprint
426+
result *graph.CertNode
427+
err error
428+
}
429+
430+
certChan := make(chan fingerprint.Fingerprint, len(fingerprints))
431+
resultChan := make(chan certWork, len(fingerprints))
432+
433+
// Start worker goroutines
434+
numWorkers := min(config.parallel, uint(len(fingerprints)))
435+
if numWorkers == 0 {
436+
numWorkers = 1
437+
}
438+
439+
for i := uint(0); i < numWorkers; i++ {
440+
go func() {
441+
for fp := range certChan {
442+
var work certWork
443+
work.fp = fp
444+
445+
// Check if we've already attempted to process this certificate
446+
processedCertsMutex.Lock()
447+
if processedCerts[fp] {
448+
processedCertsMutex.Unlock()
449+
work.err = fmt.Errorf("already processed")
450+
resultChan <- work
451+
continue
452+
}
453+
processedCerts[fp] = true
454+
processedCertsMutex.Unlock()
431455

432-
// Mark as being processed to avoid duplicate attempts
433-
processedCerts[fp] = true
456+
// get cert details
457+
certResult, err := results.QueryCert(ctx, fp)
458+
if err != nil {
459+
work.err = err
460+
resultChan <- work
461+
continue
462+
}
434463

435-
// get cert details
436-
certResult, err := results.QueryCert(ctx, fp)
437-
if err != nil {
438-
v("QueryCert", err)
439-
continue
464+
work.result = certNodeFromCertResult(certResult)
465+
resultChan <- work
440466
}
441-
442-
certNode = certNodeFromCertResult(certResult)
443-
certGraph.AddCert(certNode)
467+
}()
468+
}
469+
470+
// Send work to workers
471+
workCount := 0
472+
for _, fp := range fingerprints {
473+
// Check if cert already exists in graph
474+
if _, exists := certGraph.GetCert(fp); exists {
475+
continue
476+
}
477+
certChan <- fp
478+
workCount++
479+
}
480+
close(certChan)
481+
482+
// Collect results
483+
for i := 0; i < workCount; i++ {
484+
work := <-resultChan
485+
if work.err != nil {
486+
if work.err.Error() != "already processed" {
487+
v("QueryCert", work.err)
488+
}
489+
continue
490+
}
491+
492+
if work.result != nil {
493+
certGraph.AddCert(work.result)
494+
}
495+
}
496+
497+
// Add relationships after all certificates are processed
498+
for _, fp := range fingerprints {
499+
certNode, exists := certGraph.GetCert(fp)
500+
if exists {
501+
certNode.AddFound(certDriver.GetName())
502+
domainNode.AddCertFingerprint(certNode.Fingerprint, certDriver.GetName())
444503
}
445-
446-
certNode.AddFound(certDriver.GetName())
447-
domainNode.AddCertFingerprint(certNode.Fingerprint, certDriver.GetName())
448504
}
449505

450506
// we don't process any other certificates returned, they will be collected
@@ -460,10 +516,12 @@ func printNode(domainNode *graph.DomainNode) {
460516
_, _ = fmt.Fprintln(os.Stdout, domainNode.Domain)
461517
}
462518
if config.checkDNS && !domainNode.HasDNS {
463-
// TODO print this in a better way
464-
// TODO for debugging
465519
realDomain, _ := dns.ApexDomain(domainNode.Domain)
466-
_, _ = fmt.Fprintf(os.Stdout, "* Missing DNS for: %s\n", realDomain)
520+
if config.details {
521+
_, _ = fmt.Fprintf(os.Stdout, " ⚠ DNS resolution failed for apex domain: %s\n", realDomain)
522+
} else {
523+
_, _ = fmt.Fprintf(os.Stdout, " [NO DNS] %s\n", realDomain)
524+
}
467525

468526
}
469527
}

driver/multi/multi_driver.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,16 +71,19 @@ func newResult(host string) *multiResult {
7171
r.host = host
7272
r.results = make([]driver.Result, 0, 2)
7373
r.fingerprints = make(driver.FingerprintMap)
74+
r.seenFPs = make(map[fingerprint.Fingerprint]bool)
7475
return r
7576
}
7677

7778
// multiResult aggregates results from multiple drivers for a single domain query.
7879
// It provides thread-safe access to merged certificate fingerprints and related data.
7980
type multiResult struct {
80-
host string // The queried domain
81-
results []driver.Result // Results from individual drivers
82-
resultLock sync.Mutex // Protects results and fingerprints maps
83-
fingerprints driver.FingerprintMap // Merged fingerprints from all drivers
81+
host string // The queried domain
82+
results []driver.Result // Results from individual drivers
83+
resultLock sync.Mutex // Protects results and fingerprints maps
84+
fingerprints driver.FingerprintMap // Merged fingerprints from all drivers
85+
seenFPs map[fingerprint.Fingerprint]bool // Tracks fingerprints to prevent duplicates
86+
driverStatuses []status.Map // Individual driver status reports
8487
}
8588

8689
// add merges a driver result into this multiResult instance.
@@ -94,11 +97,16 @@ func (c *multiResult) add(r driver.Result) error {
9497
}
9598
for domain := range fpm {
9699
for _, fp := range fpm[domain] {
97-
// TODO does not dedupe across drivers
98-
c.fingerprints.Add(domain, fp)
100+
// Check if we've already seen this fingerprint to dedupe across drivers
101+
if !c.seenFPs[fp] {
102+
c.seenFPs[fp] = true
103+
c.fingerprints.Add(domain, fp)
104+
}
99105
}
100106
}
101107

108+
// Store individual driver status
109+
c.driverStatuses = append(c.driverStatuses, r.GetStatus())
102110
c.results = append(c.results, r)
103111
return nil
104112
}
@@ -123,10 +131,25 @@ func (c *multiResult) GetFingerprints() (driver.FingerprintMap, error) {
123131
return c.fingerprints, nil
124132
}
125133

126-
// GetStatus returns a status map indicating this is a multi-driver result.
127-
// TODO: Consider nesting individual driver statuses for more detailed reporting.
134+
// GetStatus returns a comprehensive status map including individual driver statuses.
135+
// Provides detailed reporting for each driver used in the multi-driver query.
128136
func (c *multiResult) GetStatus() status.Map {
129-
return status.NewMap(c.host, status.New(status.MULTI))
137+
c.resultLock.Lock()
138+
defer c.resultLock.Unlock()
139+
140+
// Create main multi-driver status
141+
multiStatus := status.NewMap(c.host, status.New(status.MULTI))
142+
143+
// Add individual driver statuses with prefixed keys for identification
144+
for i, driverStatus := range c.driverStatuses {
145+
for domain, stat := range driverStatus {
146+
// Prefix with driver index to avoid conflicts
147+
key := fmt.Sprintf("%s_driver_%d", domain, i)
148+
multiStatus[key] = stat
149+
}
150+
}
151+
152+
return multiStatus
130153
}
131154

132155
// GetRelated returns a deduplicated list of related domains from all drivers.

0 commit comments

Comments
 (0)