├── README.md └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # dumpcn 2 | 3 | A tool that takes a list of domains from standard input (prefixed with the 4 | protocol or not), grabs the certificate and prints the CN and SANs. 5 | 6 | It runs on 32 threads (goroutines) by default. 7 | 8 | 9 | Running it against 10 | [opendns-top-domains.txt](https://raw.githubusercontent.com/opendns/public-domain-lists/master/opendns-top-domains.txt) 11 | (10000 domains) takes approximately 7 seconds: 12 | ``` 13 | $ time ./dumpcn -t=100 < opendns-top-domains.txt > /dev/null 14 | ./dumpcn -t=100 < opendns-top-domains.txt > /dev/null 1,66s user 1,24s system 39% cpu 7,311 total 15 | ``` 16 | 17 | ## Installation 18 | 19 | ``` 20 | $ go get -u github.com/samirettali/dumpcn 21 | ``` 22 | 23 | ## Usage 24 | 25 | ``` 26 | $ cat domains.txt | ./dumpcn 27 | ``` 28 | 29 | Change number of threads: 30 | ``` 31 | $ cat domains.txt | ./dumpcn -t=100 32 | ``` 33 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "net" 10 | "net/http" 11 | "os" 12 | "strings" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | func worker(jobChan <-chan string, resChan chan<- string, wg *sync.WaitGroup, includeSANs bool) { 18 | defer wg.Done() 19 | 20 | var transport = &http.Transport{ 21 | Dial: (&net.Dialer{ 22 | Timeout: 5 * time.Second, 23 | }).Dial, 24 | TLSHandshakeTimeout: 5 * time.Second, 25 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 26 | } 27 | 28 | var client = &http.Client{ 29 | Timeout: time.Second * 10, 30 | Transport: transport, 31 | } 32 | 33 | for job := range jobChan { 34 | if !strings.HasPrefix(job, "https://") { 35 | job = "https://" + job 36 | } else if strings.HasPrefix(job, "http://") { 37 | job = strings.Replace(job, "http://", "https://", 1) 38 | } 39 | 40 | req, reqErr := http.NewRequest("HEAD", job, nil) 41 | if reqErr != nil { 42 | continue 43 | } 44 | 45 | resp, clientErr := client.Do(req) 46 | if clientErr != nil { 47 | continue 48 | } 49 | 50 | if resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 { 51 | if includeSANs { 52 | dnsNames := resp.TLS.PeerCertificates[0].DNSNames 53 | for _, name := range dnsNames { 54 | resChan <- string(name) 55 | } 56 | } 57 | resChan <- resp.TLS.PeerCertificates[0].Subject.CommonName 58 | } 59 | } 60 | 61 | } 62 | func main() { 63 | workers := flag.Int("t", 32, "numbers of threads") 64 | includeSANs := flag.Bool("s", false, "print SANs") 65 | flag.Parse() 66 | 67 | scanner := bufio.NewScanner(os.Stdin) 68 | jobChan := make(chan string) 69 | resChan := make(chan string) 70 | done := make(chan struct{}) 71 | 72 | var wg sync.WaitGroup 73 | wg.Add(*workers) 74 | 75 | go func() { 76 | wg.Wait() 77 | close(done) 78 | }() 79 | 80 | for i := 0; i < *workers; i++ { 81 | go worker(jobChan, resChan, &wg, *includeSANs) 82 | } 83 | 84 | go func() { 85 | for scanner.Scan() { 86 | jobChan <- scanner.Text() 87 | } 88 | if err := scanner.Err(); err != nil { 89 | log.Println(err) 90 | } 91 | close(jobChan) 92 | }() 93 | 94 | for { 95 | select { 96 | case <-done: 97 | return 98 | case res := <-resChan: 99 | fmt.Println(res) 100 | } 101 | } 102 | } 103 | --------------------------------------------------------------------------------