-
Notifications
You must be signed in to change notification settings - Fork 3
/
main.go
131 lines (116 loc) · 2.86 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package main
import (
"bufio"
"crypto/tls"
"encoding/json"
"flag"
"fmt"
"net"
"os"
"strings"
"sync"
"time"
)
/*
CertData represents the extracted certificate data for a domain.
*/
type CertData struct {
Domain string `json:"domain"`
CommonName string `json:"common_name"`
Org []string `json:"org"`
DNSNames []string `json:"dns_names"`
}
func main() {
/*
Parse command line flags to determine the number of concurrent tasks.
*/
concurrency := flag.Int("c", 100, "Number of concurrent tasks")
flag.Parse()
var wg sync.WaitGroup
// Create channels to communicate between goroutines.
domains := make(chan string, *concurrency) // Channel to send domain names.
results := make(chan *CertData, *concurrency) // Channel to receive certificate data.
/*
Start worker goroutines.
*/
for i := 0; i < *concurrency; i++ {
wg.Add(1)
go func() {
defer wg.Done()
/*
Process domain names from the 'domains' channel.
*/
for domain := range domains {
func() {
defer recover() // Catch any panics.
/*
Fetch certificate data for the current domain.
*/
data, err := getCertificate(domain)
if err == nil && data != nil {
results <- data // Send the certificate data to the 'results' channel.
}
}()
}
}()
}
/*
Read domain names from standard input and send them to the 'domains' channel.
*/
go func() {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
domain := strings.TrimSpace(scanner.Text())
domain = strings.TrimPrefix(domain, "http://")
domain = strings.TrimPrefix(domain, "https://")
domains <- domain // Send the domain to the 'domains' channel.
}
close(domains) // Close the 'domains' channel when all domains are sent.
}()
/*
Wait for all worker goroutines to finish.
*/
go func() {
wg.Wait()
close(results) // Close the 'results' channel when all workers are done.
}()
first := true
/*
Process certificate data received from the 'results' channel.
*/
for result := range results {
if !first {
fmt.Println() // Print a newline to separate JSON objects.
} else {
first = false
}
output, err := json.Marshal(result)
if err == nil {
fmt.Print(string(output))
}
}
}
/*
getCertificate fetches SSL/TLS certificate data for the given domain.
*/
func getCertificate(domain string) (*CertData, error) {
dialer := &net.Dialer{
Timeout: 5 * time.Second,
}
conn, err := tls.DialWithDialer(dialer, "tcp", domain+":443", &tls.Config{
InsecureSkipVerify: true,
})
if err != nil {
return nil, err
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(5 * time.Second))
cert := conn.ConnectionState().PeerCertificates[0]
certData := &CertData{
Domain: domain,
CommonName: cert.Subject.CommonName,
Org: cert.Subject.Organization,
DNSNames: cert.DNSNames,
}
return certData, nil
}