├── static └── revit.png ├── go.mod ├── internal └── revit │ ├── types.go │ ├── lookup.go │ ├── stdin.go │ └── file.go ├── go.sum ├── LICENSE ├── README.md └── cmd └── revit └── main.go /static/revit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devanshbatham/revit/HEAD/static/revit.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/devanshbatham/revit 2 | 3 | go 1.20 4 | 5 | require github.com/fatih/color v1.15.0 6 | 7 | require ( 8 | github.com/mattn/go-colorable v0.1.13 // indirect 9 | github.com/mattn/go-isatty v0.0.17 // indirect 10 | golang.org/x/sys v0.6.0 // indirect 11 | github.com/devanshbatham/revit/internal/revit v0.0.1 12 | ) 13 | -------------------------------------------------------------------------------- /internal/revit/types.go: -------------------------------------------------------------------------------- 1 | package revit 2 | 3 | // LookupResult represents the result of a reverse DNS lookup. 4 | type LookupResult struct { 5 | IPAddress string // The IP address for which the lookup was performed 6 | DNSNames []string // List of resolved DNS names associated with the IP address 7 | Error error // Any error that occurred during the lookup process 8 | } 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= 2 | github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= 3 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 4 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 5 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 6 | github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= 7 | github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 8 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 9 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 10 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 11 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /internal/revit/lookup.go: -------------------------------------------------------------------------------- 1 | package revit 2 | 3 | import ( 4 | "net" 5 | "context" 6 | ) 7 | 8 | // LookupAddr performs reverse DNS lookup for the given IP address using the specified resolver. 9 | // It sends the lookup results (IP address, resolved DNS names, and any errors) to the results channel. 10 | func LookupAddr(ip string, results chan<- LookupResult, sem chan struct{}, resolver string) { 11 | // Acquire a semaphore to limit the level of concurrency 12 | sem <- struct{}{} 13 | defer func() { <-sem }() 14 | 15 | // Initialize a net.Resolver for performing DNS lookups 16 | var r *net.Resolver 17 | if resolver != "" { 18 | // Create a custom dialer for the resolver 19 | dialer := &net.Dialer{} 20 | r = &net.Resolver{ 21 | PreferGo: true, 22 | Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 23 | return dialer.DialContext(ctx, "udp", resolver+":53") 24 | }, 25 | } 26 | } else { 27 | r = net.DefaultResolver 28 | } 29 | 30 | // Perform a reverse DNS lookup using the Resolver 31 | names, err := r.LookupAddr(context.Background(), ip) 32 | 33 | // Send the lookup results (IP address, resolved DNS names, and errors) to the results channel 34 | results <- LookupResult{ 35 | IPAddress: ip, 36 | DNSNames: names, 37 | Error: err, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/revit/stdin.go: -------------------------------------------------------------------------------- 1 | package revit 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "sync" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | // ProcessStdin reads IP addresses from the standard input (stdin), performs DNS lookup 13 | // for each IP, and sends the results to the results channel. 14 | func ProcessStdin(results chan<- LookupResult, sem chan struct{}, wg *sync.WaitGroup, resolvers []string) { 15 | // Create a scanner to read input from the standard input (stdin) 16 | scanner := bufio.NewScanner(os.Stdin) 17 | 18 | // Seed the random number generator with the current time 19 | rand.Seed(time.Now().UnixNano()) 20 | 21 | // Loop through each line in the input 22 | for scanner.Scan() { 23 | ip := scanner.Text() 24 | 25 | // Increment the wait group count for each IP address 26 | wg.Add(1) 27 | 28 | // Launch a goroutine to perform DNS lookup for the IP address 29 | go func(ipAddress string) { 30 | defer wg.Done() 31 | 32 | // Select a random resolver from the provided list 33 | selectedResolver := "" 34 | if len(resolvers) > 0 { 35 | selectedResolver = resolvers[rand.Intn(len(resolvers))] 36 | } 37 | 38 | // Call the LookupAddr function to perform reverse DNS lookup 39 | LookupAddr(ipAddress, results, sem, selectedResolver) 40 | }(ip) 41 | } 42 | 43 | // Check for errors while reading from stdin 44 | if err := scanner.Err(); err != nil { 45 | fmt.Fprintf(os.Stderr, "Failed to read from stdin: %v\n", err) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /internal/revit/file.go: -------------------------------------------------------------------------------- 1 | package revit 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "sync" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | // ProcessFile reads IP addresses from a file, performs DNS lookup for each IP, 13 | // and sends the results to the results channel. 14 | func ProcessFile(filename string, results chan<- LookupResult, sem chan struct{}, wg *sync.WaitGroup, resolvers []string) { 15 | // Open the specified file for reading IP addresses 16 | file, err := os.Open(filename) 17 | if err != nil { 18 | fmt.Fprintf(os.Stderr, "Failed to open file: %v\n", err) 19 | return 20 | } 21 | defer file.Close() 22 | 23 | // Seed the random number generator with the current time 24 | rand.Seed(time.Now().UnixNano()) 25 | 26 | // Create a scanner to read IP addresses from the file line by line 27 | scanner := bufio.NewScanner(file) 28 | for scanner.Scan() { 29 | ip := scanner.Text() 30 | 31 | // Increment the wait group count for each IP address 32 | wg.Add(1) 33 | 34 | // Launch a goroutine to perform DNS lookup for the IP address 35 | go func(ipAddress string) { 36 | defer wg.Done() 37 | 38 | // Select a random resolver from the provided list 39 | selectedResolver := "" 40 | if len(resolvers) > 0 { 41 | selectedResolver = resolvers[rand.Intn(len(resolvers))] 42 | } 43 | 44 | // Call the LookupAddr function to perform reverse DNS lookup 45 | LookupAddr(ipAddress, results, sem, selectedResolver) 46 | }(ip) 47 | } 48 | 49 | // Check for errors while scanning the file 50 | if err := scanner.Err(); err != nil { 51 | fmt.Fprintf(os.Stderr, "Failed to read file: %v\n", err) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | revit 3 |
4 |

5 | 6 |

A command-line utility for performing reverse DNS lookups

7 | 8 | 9 |

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

14 | 15 | 16 | ![revit](https://github.com/devanshbatham/revit/blob/main/static/revit.png?raw=true) 17 | 18 | # Install 19 | To install revit, run the following command: 20 | 21 | ```sh 22 | go install github.com/devanshbatham/revit/cmd/revit@v0.0.1 23 | ``` 24 | 25 | 26 | # Usage 27 | This utility allows you to perform reverse DNS lookups on IP addresses. Here are some examples of how to use the tool: 28 | 29 | - Look up a single IP address: 30 | ```sh 31 | revit -i "8.8.8.8" 32 | ``` 33 | 34 | - Look up a list of IP addresses from a file: 35 | ```sh 36 | revit -l ip_list.txt 37 | ``` 38 | 39 | - Pipe input from another command: 40 | ```sh 41 | echo "8.8.8.8" | revit 42 | ``` 43 | 44 | ```sh 45 | cat ip_list.txt | revit 46 | ``` 47 | 48 | Here are the available command-line flags: 49 | 50 | | Flag | Description | Example | 51 | |-------------|--------------------------------------------------------------------|----------------------------| 52 | | `-i` | Specify a single target IP address for reverse DNS lookup. | `revit -i 8.8.8.8` | 53 | | `-l` | Provide the path to a file containing a list of IP addresses. | `revit -l ip_list.txt` | 54 | | `-c` | Set the level of concurrency for concurrent DNS lookups (default: 10). | `revit -c 20` | 55 | | `-r` | Specify resolvers for reverse DNS lookup. | `revit -r 8.8.8.8` | 56 | | | You can provide a single IP address or a path to a file. | `revit -r resolvers.txt` | 57 | 58 | 59 | 60 | # Inspiration 61 | 62 | **revit** was born out of curiosity and a desire to explore Golang. While there are existing tools like [hakrevdns](https://github.com/hakluke/hakrevdns) that perform similar tasks (and I have immense respect for them), I decided to create this utility as a personal project to further my understanding of Go and enhance my programming skills. 63 | 64 | The development of **revit** started as an exploration into concurrent programming and networking in Go. As I tinkered with the language's features and learned more about its capabilities, the utility began to take shape. 65 | -------------------------------------------------------------------------------- /cmd/revit/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "math/rand" 8 | "os" 9 | "sync" 10 | "strings" 11 | "time" 12 | "github.com/devanshbatham/revit/internal/revit" 13 | "github.com/fatih/color" 14 | "log" 15 | ) 16 | 17 | func main() { 18 | // Command-line flags 19 | var ( 20 | ips = flag.String("i", "", "target IP to scan (-i IP_ADDRESS)") 21 | list = flag.String("l", "", "target list of IPs to scan (-l INPUT_FILE)") 22 | concurrency = flag.Int("c", 10, "level of concurrency (-c 20)") 23 | resolvers = flag.String("r", "", "resolvers for reverse DNS lookup (-r 8.8.8.8 or -r resolvers.txt)") 24 | ) 25 | 26 | // Parse command-line flags 27 | flag.Parse() 28 | log.SetFlags(0) 29 | 30 | // Print tool banner 31 | log.Print(` 32 | _ __ 33 | ________ _ __(_) /_ 34 | / ___/ _ \ | / / / __/ 35 | / / / __/ |/ / / /_ 36 | /_/ \___/|___/_/\__/ 37 | 38 | - A Reverse DNS Lookup Utility 39 | 40 | `) 41 | 42 | // Process resolver list 43 | var resolverList []string 44 | if *resolvers != "" { 45 | if strings.Contains(*resolvers, ".txt") { 46 | resolverList = readResolversFromFile(*resolvers) 47 | } else { 48 | resolverList = []string{*resolvers} 49 | } 50 | } 51 | 52 | // Detect if there's piped input 53 | info, err := os.Stdin.Stat() 54 | isPipedInput := err == nil && info.Mode()&os.ModeNamedPipe != 0 55 | 56 | // Initialize wait group, results channel, and semaphore 57 | var wg sync.WaitGroup 58 | results := make(chan revit.LookupResult) 59 | sem := make(chan struct{}, *concurrency) 60 | 61 | rand.Seed(time.Now().UnixNano()) 62 | 63 | // Perform DNS lookup 64 | doLookup := func(ip string) { 65 | selectedResolver := "" 66 | if len(resolverList) > 0 { 67 | selectedResolver = resolverList[rand.Intn(len(resolverList))] 68 | } 69 | revit.LookupAddr(ip, results, sem, selectedResolver) 70 | } 71 | 72 | // Handle command-line flags and piped input 73 | if *ips != "" { 74 | wg.Add(1) 75 | go func() { 76 | defer wg.Done() 77 | doLookup(*ips) 78 | }() 79 | } else if *list != "" { 80 | // Handle IP list from a file 81 | file, err := os.Open(*list) 82 | if err != nil { 83 | fmt.Fprintf(os.Stderr, "Failed to open file: %v\n", err) 84 | return 85 | } 86 | defer file.Close() 87 | 88 | scanner := bufio.NewScanner(file) 89 | for scanner.Scan() { 90 | ip := scanner.Text() 91 | wg.Add(1) 92 | go func(ipAddress string) { 93 | defer wg.Done() 94 | doLookup(ipAddress) 95 | }(ip) 96 | } 97 | } else if isPipedInput { 98 | // Handle piped input 99 | scanner := bufio.NewScanner(os.Stdin) 100 | for scanner.Scan() { 101 | ip := scanner.Text() 102 | wg.Add(1) 103 | go func(ipAddress string) { 104 | defer wg.Done() 105 | doLookup(ipAddress) 106 | }(ip) 107 | } 108 | } else { 109 | flag.Usage() // Display usage if no flags provided 110 | return 111 | } 112 | 113 | // Wait for DNS lookups to complete 114 | go func() { 115 | wg.Wait() 116 | close(results) 117 | }() 118 | 119 | // Color formatting functions 120 | yellow := color.New(color.FgYellow).SprintFunc() 121 | green := color.New(color.FgGreen).SprintFunc() 122 | 123 | // Process and display lookup results 124 | for res := range results { 125 | if res.Error != nil && res.Error.Error() == "no such host" { 126 | continue 127 | } 128 | for _, name := range res.DNSNames { 129 | name = strings.TrimSuffix(name, ".") 130 | ipPadding := " " 131 | if len(res.IPAddress) < 15 { 132 | ipPadding = strings.Repeat(" ", 15-len(res.IPAddress)) 133 | } 134 | 135 | // Print formatted IP address and resolved name 136 | fmt.Printf("%s%s [%s]\n", yellow(res.IPAddress), ipPadding, green(name)) 137 | } 138 | } 139 | } 140 | 141 | // Read resolvers from a file 142 | func readResolversFromFile(filename string) []string { 143 | file, err := os.Open(filename) 144 | if err != nil { 145 | fmt.Fprintf(os.Stderr, "Failed to open resolvers file: %v\n", err) 146 | return nil 147 | } 148 | defer file.Close() 149 | 150 | var resolvers []string 151 | scanner := bufio.NewScanner(file) 152 | for scanner.Scan() { 153 | resolvers = append(resolvers, scanner.Text()) 154 | } 155 | 156 | if err := scanner.Err(); err != nil { 157 | fmt.Fprintf(os.Stderr, "Failed to read resolvers file: %v\n", err) 158 | } 159 | 160 | return resolvers 161 | } 162 | --------------------------------------------------------------------------------