package runner import ( "flag" "os" "path" "reflect" "strings" "github.com/projectdiscovery/gologger" ) // Options contains the configuration options for tuning // the subdomain enumeration process. type Options struct { Verbose bool // Verbose flag indicates whether to show verbose output or not NoColor bool // No-Color disables the colored output ChaosUpload bool // ChaosUpload indicates whether to upload results to the Chaos API JSON bool // JSON specifies whether to use json for output format or text file HostIP bool // HostIP specifies whether to write subdomains in host:ip format Silent bool // Silent suppresses any extra text and only writes subdomains to screen ListSources bool // ListSources specifies whether to list all available sources RemoveWildcard bool // RemoveWildcard specifies whether to remove potential wildcard or dead subdomains from the results. Stdin bool // Stdin specifies whether stdin input was given to the process Version bool // Version specifies if we should just show version and exit Threads int // Thread controls the number of threads to use for active enumerations Timeout int // Timeout is the seconds to wait for sources to respond MaxEnumerationTime int // MaxEnumerationTime is the maximum amount of time in mins to wait for enumeration Domain string // Domain is the domain to find subdomains for DomainsFile string // DomainsFile is the file containing list of domains to find subdomains for Output string // Output is the file to write found subdomains to. OutputDirectory string // OutputDirectory is the directory to write results to in case list of domains is given Sources string // Sources contains a comma-separated list of sources to use for enumeration ExcludeSources string // ExcludeSources contains the comma-separated sources to not include in the enumeration process Resolvers string // Resolvers is the comma-separated resolvers to use for enumeration ResolverList string // ResolverList is a text file containing list of resolvers to use for enumeration ConfigFile string // ConfigFile contains the location of the config file YAMLConfig ConfigFile // YAMLConfig contains the unmarshalled yaml config file } // ParseOptions parses the command line flags provided by a user func ParseOptions() *Options { options := &Options{} config, err := GetConfigDirectory() if err != nil { // This should never be reached gologger.Fatalf("Could not get user home: %s\n", err) } flag.BoolVar(&options.Verbose, "v", false, "Show Verbose output") flag.BoolVar(&options.NoColor, "nC", false, "Don't Use colors in output") flag.IntVar(&options.Threads, "t", 10, "Number of concurrent goroutines for resolving") flag.IntVar(&options.Timeout, "timeout", 30, "Seconds to wait before timing out") flag.IntVar(&options.MaxEnumerationTime, "max-time", 10, "Minutes to wait for enumeration results") flag.StringVar(&options.Domain, "d", "", "Domain to find subdomains for") flag.StringVar(&options.DomainsFile, "dL", "", "File containing list of domains to enumerate") flag.BoolVar(&options.ChaosUpload, "cd", false, "Upload results to the Chaos API (api-key required)") flag.StringVar(&options.Output, "o", "", "File to write output to (optional)") flag.StringVar(&options.OutputDirectory, "oD", "", "Directory to write enumeration results to (optional)") flag.BoolVar(&options.JSON, "oJ", false, "Write output in JSON lines Format") flag.BoolVar(&options.HostIP, "oI", false, "Write output in Host,IP format") flag.BoolVar(&options.Silent, "silent", false, "Show only subdomains in output") flag.StringVar(&options.Sources, "sources", "", "Comma separated list of sources to use") flag.BoolVar(&options.ListSources, "ls", false, "List all available sources") flag.StringVar(&options.ExcludeSources, "exclude-sources", "", "List of sources to exclude from enumeration") flag.StringVar(&options.Resolvers, "r", "", "Comma-separated list of resolvers to use") flag.StringVar(&options.ResolverList, "rL", "", "Text file containing list of resolvers to use") flag.BoolVar(&options.RemoveWildcard, "nW", false, "Remove Wildcard & Dead Subdomains from output") flag.StringVar(&options.ConfigFile, "config", path.Join(config, "config.yaml"), "Configuration file for API Keys, etc") flag.BoolVar(&options.Version, "version", false, "Show version of subfinder") flag.Parse() // Check if stdin pipe was given options.Stdin = hasStdin() // Read the inputs and configure the logging options.configureOutput() // Show the user the banner showBanner() if options.Version { gologger.Infof("Current Version: %s\n", Version) os.Exit(0) } // Check if the config file exists. If not, it means this is the // first run of the program. Show the first run notices and initialize the config file. // Else show the normal banners and read the yaml fiile to the config if !CheckConfigExists(options.ConfigFile) { options.firstRunTasks() } else { options.normalRunTasks() } if options.ListSources { listSources(options) os.Exit(0) } // Validate the options passed by the user and if any // invalid options have been used, exit. err = options.validateOptions() if err != nil { gologger.Fatalf("Program exiting: %s\n", err) } return options } func hasStdin() bool { fi, err := os.Stdin.Stat() if err != nil { return false } if fi.Mode()&os.ModeNamedPipe == 0 { return false } return true } func listSources(options *Options) { gologger.Infof("Current list of available sources. [%d]\n", len(options.YAMLConfig.Sources)) gologger.Infof("Sources marked with an * needs key or token in order to work.\n") gologger.Infof("You can modify %s to configure your keys / tokens.\n\n", options.ConfigFile) keys := options.YAMLConfig.GetKeys() needsKey := make(map[string]interface{}) keysElem := reflect.ValueOf(&keys).Elem() for i := 0; i < keysElem.NumField(); i++ { needsKey[strings.ToLower(keysElem.Type().Field(i).Name)] = keysElem.Field(i).Interface() } for _, source := range options.YAMLConfig.Sources { message := "%s\n" if _, ok := needsKey[source]; ok { message = "%s *\n" } gologger.Silentf(message, source) } }