├── static └── banner.png ├── LICENSE ├── README.md └── main.go /static/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devanshbatham/getsan/HEAD/static/banner.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Devansh Batham 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | getsan 3 |
4 |

5 | 6 |

A utility to fetch and display dns names from the SSL/TLS cert data

7 | 8 | 9 |

10 | 🏗️ Installation 11 | ⛏️ Usage 12 |
13 |

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