2024-03-14 03:08:53 +05:30
|
|
|
// package list implements a hybrid hmap/filekv backed input provider
|
2021-10-27 15:53:04 +05:30
|
|
|
// for nuclei that can either stream or store results using different kv stores.
|
2024-03-14 03:08:53 +05:30
|
|
|
package list
|
2021-10-27 15:53:04 +05:30
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
2023-05-09 03:57:56 +05:30
|
|
|
"context"
|
2023-01-11 22:50:57 +05:30
|
|
|
"fmt"
|
2021-10-27 15:53:04 +05:30
|
|
|
"io"
|
|
|
|
|
"os"
|
2023-11-28 18:09:39 +03:00
|
|
|
"regexp"
|
2021-10-27 15:53:04 +05:30
|
|
|
"strings"
|
2022-12-20 11:07:48 +01:00
|
|
|
"sync"
|
2022-06-30 14:21:54 +02:00
|
|
|
"time"
|
2021-10-27 15:53:04 +05:30
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2021-11-25 17:09:20 +02:00
|
|
|
|
2021-10-27 15:53:04 +05:30
|
|
|
"github.com/projectdiscovery/gologger"
|
2022-12-20 11:07:48 +01:00
|
|
|
"github.com/projectdiscovery/hmap/filekv"
|
2021-10-27 15:53:04 +05:30
|
|
|
"github.com/projectdiscovery/hmap/store/hybrid"
|
2023-02-27 10:22:50 +01:00
|
|
|
"github.com/projectdiscovery/mapcidr/asn"
|
2024-03-14 03:08:53 +05:30
|
|
|
providerTypes "github.com/projectdiscovery/nuclei/v3/pkg/input/types"
|
2023-10-17 17:44:13 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
|
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
|
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/uncover"
|
|
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
2024-01-04 21:18:20 +01:00
|
|
|
"github.com/projectdiscovery/nuclei/v3/pkg/utils/expand"
|
2023-05-09 03:57:56 +05:30
|
|
|
uncoverlib "github.com/projectdiscovery/uncover"
|
2022-11-06 21:24:23 +01:00
|
|
|
fileutil "github.com/projectdiscovery/utils/file"
|
|
|
|
|
iputil "github.com/projectdiscovery/utils/ip"
|
2022-12-03 07:10:57 +05:30
|
|
|
readerutil "github.com/projectdiscovery/utils/reader"
|
2022-11-09 14:18:56 +01:00
|
|
|
sliceutil "github.com/projectdiscovery/utils/slice"
|
2023-01-11 22:50:57 +05:30
|
|
|
urlutil "github.com/projectdiscovery/utils/url"
|
2021-10-27 15:53:04 +05:30
|
|
|
)
|
|
|
|
|
|
2022-12-20 11:07:48 +01:00
|
|
|
const DefaultMaxDedupeItemsCount = 10000
|
|
|
|
|
|
2024-03-14 03:08:53 +05:30
|
|
|
// ListInputProvider is a hmap/filekv backed nuclei ListInputProvider provider
|
|
|
|
|
// it supports list type of input ex: urls,file,stdin,uncover,etc. (i.e just url not complete request/response)
|
|
|
|
|
type ListInputProvider struct {
|
2022-12-20 11:07:48 +01:00
|
|
|
ipOptions *ipOptions
|
|
|
|
|
inputCount int64
|
2023-11-23 19:35:20 +01:00
|
|
|
excludedCount int64
|
2022-12-20 11:07:48 +01:00
|
|
|
dupeCount int64
|
2023-11-23 19:35:20 +01:00
|
|
|
skippedCount int64
|
2022-12-20 11:07:48 +01:00
|
|
|
hostMap *hybrid.HybridMap
|
2023-11-23 19:35:20 +01:00
|
|
|
excludedHosts map[string]struct{}
|
2022-12-20 11:07:48 +01:00
|
|
|
hostMapStream *filekv.FileDB
|
|
|
|
|
hostMapStreamOnce sync.Once
|
|
|
|
|
sync.Once
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
|
|
|
|
|
2022-12-21 22:48:43 +05:30
|
|
|
// Options is a wrapper around types.Options structure
|
|
|
|
|
type Options struct {
|
|
|
|
|
// Options contains options for hmap provider
|
|
|
|
|
Options *types.Options
|
|
|
|
|
// NotFoundCallback is called for each not found target
|
|
|
|
|
// This overrides error handling for not found target
|
|
|
|
|
NotFoundCallback func(template string) bool
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-27 15:53:04 +05:30
|
|
|
// New creates a new hmap backed nuclei Input Provider
|
|
|
|
|
// and initializes it based on the passed options Model.
|
2024-03-14 03:08:53 +05:30
|
|
|
func New(opts *Options) (*ListInputProvider, error) {
|
2022-12-21 22:48:43 +05:30
|
|
|
options := opts.Options
|
|
|
|
|
|
2021-10-27 15:53:04 +05:30
|
|
|
hm, err := hybrid.New(hybrid.DefaultDiskOptions)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not create temporary input file")
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 03:08:53 +05:30
|
|
|
input := &ListInputProvider{
|
2022-11-09 14:18:56 +01:00
|
|
|
hostMap: hm,
|
|
|
|
|
ipOptions: &ipOptions{
|
|
|
|
|
ScanAllIPs: options.ScanAllIPs,
|
|
|
|
|
IPV4: sliceutil.Contains(options.IPVersion, "4"),
|
|
|
|
|
IPV6: sliceutil.Contains(options.IPVersion, "6"),
|
|
|
|
|
},
|
2023-11-23 19:35:20 +01:00
|
|
|
excludedHosts: make(map[string]struct{}),
|
2022-11-09 14:18:56 +01:00
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
if options.Stream {
|
|
|
|
|
fkvOptions := filekv.DefaultOptions
|
2022-12-20 11:07:48 +01:00
|
|
|
fkvOptions.MaxItems = DefaultMaxDedupeItemsCount
|
2021-10-27 15:53:04 +05:30
|
|
|
if tmpFileName, err := fileutil.GetTempFileName(); err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not create temporary input file")
|
|
|
|
|
} else {
|
|
|
|
|
fkvOptions.Path = tmpFileName
|
|
|
|
|
}
|
|
|
|
|
fkv, err := filekv.Open(fkvOptions)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not create temporary unsorted input file")
|
|
|
|
|
}
|
|
|
|
|
input.hostMapStream = fkv
|
|
|
|
|
}
|
2022-12-21 22:48:43 +05:30
|
|
|
if initErr := input.initializeInputSources(opts); initErr != nil {
|
2021-10-27 15:53:04 +05:30
|
|
|
return nil, initErr
|
|
|
|
|
}
|
2023-11-23 19:35:20 +01:00
|
|
|
if input.excludedCount > 0 {
|
|
|
|
|
gologger.Info().Msgf("Number of hosts excluded from input: %d", input.excludedCount)
|
|
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
if input.dupeCount > 0 {
|
|
|
|
|
gologger.Info().Msgf("Supplied input was automatically deduplicated (%d removed).", input.dupeCount)
|
|
|
|
|
}
|
2023-11-23 19:35:20 +01:00
|
|
|
if input.skippedCount > 0 {
|
|
|
|
|
gologger.Info().Msgf("Number of hosts skipped from input due to exclusion: %d", input.skippedCount)
|
|
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
return input, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 03:08:53 +05:30
|
|
|
// Count returns the input count
|
|
|
|
|
func (i *ListInputProvider) Count() int64 {
|
|
|
|
|
return i.inputCount
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
|
|
|
|
|
2024-03-14 03:08:53 +05:30
|
|
|
// Iterate over all inputs in order
|
|
|
|
|
func (i *ListInputProvider) Iterate(callback func(value *contextargs.MetaInput) bool) {
|
|
|
|
|
if i.hostMapStream != nil {
|
|
|
|
|
i.hostMapStreamOnce.Do(func() {
|
|
|
|
|
if err := i.hostMapStream.Process(); err != nil {
|
|
|
|
|
gologger.Warning().Msgf("error in stream mode processing: %s\n", err)
|
2022-12-21 22:48:43 +05:30
|
|
|
}
|
2024-03-14 03:08:53 +05:30
|
|
|
})
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
2024-03-14 03:08:53 +05:30
|
|
|
callbackFunc := func(k, _ []byte) error {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
2024-03-14 03:08:53 +05:30
|
|
|
if err := metaInput.Unmarshal(string(k)); err != nil {
|
2022-11-16 11:12:39 +05:30
|
|
|
return err
|
|
|
|
|
}
|
2024-03-14 03:08:53 +05:30
|
|
|
if !callback(metaInput) {
|
|
|
|
|
return io.EOF
|
2023-11-23 19:35:20 +01:00
|
|
|
}
|
2024-03-14 03:08:53 +05:30
|
|
|
return nil
|
2023-11-23 19:35:20 +01:00
|
|
|
}
|
2024-03-14 03:08:53 +05:30
|
|
|
if i.hostMapStream != nil {
|
|
|
|
|
_ = i.hostMapStream.Scan(callbackFunc)
|
|
|
|
|
} else {
|
|
|
|
|
i.hostMap.Scan(callbackFunc)
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-16 11:12:39 +05:30
|
|
|
// Set normalizes and stores passed input values
|
2025-07-09 14:47:26 -05:00
|
|
|
func (i *ListInputProvider) Set(executionId string, value string) {
|
2022-11-09 14:18:56 +01:00
|
|
|
URL := strings.TrimSpace(value)
|
|
|
|
|
if URL == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-12-05 18:22:04 +05:30
|
|
|
// parse hostname if url is given
|
2023-01-24 22:04:52 +05:30
|
|
|
urlx, err := urlutil.Parse(URL)
|
|
|
|
|
if err != nil || (urlx != nil && urlx.Host == "") {
|
2023-01-11 22:50:57 +05:30
|
|
|
gologger.Debug().Label("url").MsgFunc(func() string {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Sprintf("failed to parse url %v got %v skipping ip selection", URL, err)
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("got empty hostname for %v skipping ip selection", URL)
|
|
|
|
|
})
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
|
|
|
|
metaInput.Input = URL
|
2023-01-11 22:50:57 +05:30
|
|
|
i.setItem(metaInput)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if input is ip or hostname
|
2023-01-24 22:04:52 +05:30
|
|
|
if iputil.IsIP(urlx.Hostname()) {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
|
|
|
|
metaInput.Input = URL
|
2023-01-11 22:50:57 +05:30
|
|
|
i.setItem(metaInput)
|
2022-12-24 19:03:23 +05:30
|
|
|
return
|
2022-12-05 18:22:04 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if i.ipOptions.ScanAllIPs {
|
|
|
|
|
// scan all ips
|
2025-07-09 14:47:26 -05:00
|
|
|
dialers := protocolstate.GetDialersWithId(executionId)
|
|
|
|
|
if dialers == nil {
|
|
|
|
|
panic("dialers with executionId " + executionId + " not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dnsData, err := dialers.Fastdialer.GetDNSData(urlx.Hostname())
|
2022-12-24 19:03:23 +05:30
|
|
|
if err == nil {
|
|
|
|
|
if (len(dnsData.A) + len(dnsData.AAAA)) > 0 {
|
|
|
|
|
var ips []string
|
|
|
|
|
if i.ipOptions.IPV4 {
|
|
|
|
|
ips = append(ips, dnsData.A...)
|
2022-11-09 14:18:56 +01:00
|
|
|
}
|
2022-12-24 19:03:23 +05:30
|
|
|
if i.ipOptions.IPV6 {
|
|
|
|
|
ips = append(ips, dnsData.AAAA...)
|
|
|
|
|
}
|
|
|
|
|
for _, ip := range ips {
|
|
|
|
|
if ip == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
2024-12-20 23:02:33 +01:00
|
|
|
metaInput.Input = URL
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput.CustomIP = ip
|
2022-12-24 19:03:23 +05:30
|
|
|
i.setItem(metaInput)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
} else {
|
|
|
|
|
gologger.Debug().Msgf("scanAllIps: no ip's found reverting to default")
|
2022-11-09 14:18:56 +01:00
|
|
|
}
|
2022-12-24 19:03:23 +05:30
|
|
|
} else {
|
|
|
|
|
// failed to scanallips falling back to defaults
|
|
|
|
|
gologger.Debug().Msgf("scanAllIps: dns resolution failed: %v", err)
|
2022-12-05 18:22:04 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ips := []string{}
|
|
|
|
|
// only scan the target but ipv6 if it has one
|
|
|
|
|
if i.ipOptions.IPV6 {
|
2025-07-09 14:47:26 -05:00
|
|
|
dialers := protocolstate.GetDialersWithId(executionId)
|
|
|
|
|
if dialers == nil {
|
|
|
|
|
panic("dialers with executionId " + executionId + " not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dnsData, err := dialers.Fastdialer.GetDNSData(urlx.Hostname())
|
2022-12-05 18:22:04 +05:30
|
|
|
if err == nil && len(dnsData.AAAA) > 0 {
|
|
|
|
|
// pick/ prefer 1st
|
|
|
|
|
ips = append(ips, dnsData.AAAA[0])
|
|
|
|
|
} else {
|
2022-12-24 19:03:23 +05:30
|
|
|
gologger.Warning().Msgf("target does not have ipv6 address falling back to ipv4 %v\n", err)
|
2022-12-05 18:22:04 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if i.ipOptions.IPV4 {
|
|
|
|
|
// if IPV4 is enabled do not specify ip let dialer handle it
|
|
|
|
|
ips = append(ips, "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, ip := range ips {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
2022-12-05 18:22:04 +05:30
|
|
|
if ip != "" {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput.Input = URL
|
|
|
|
|
metaInput.CustomIP = ip
|
2022-12-05 18:22:04 +05:30
|
|
|
i.setItem(metaInput)
|
|
|
|
|
} else {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput.Input = URL
|
2022-12-05 18:22:04 +05:30
|
|
|
i.setItem(metaInput)
|
2022-11-09 14:18:56 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 03:08:53 +05:30
|
|
|
// SetWithProbe only sets the input if it is live
|
2025-07-09 14:47:26 -05:00
|
|
|
func (i *ListInputProvider) SetWithProbe(executionId string, value string, probe providerTypes.InputLivenessProbe) error {
|
2024-03-14 03:08:53 +05:30
|
|
|
probedValue, err := probe.ProbeURL(value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2025-07-09 14:47:26 -05:00
|
|
|
i.Set(executionId, probedValue)
|
2024-03-14 03:08:53 +05:30
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 19:35:20 +01:00
|
|
|
// SetWithExclusions normalizes and stores passed input values if not excluded
|
2025-07-09 14:47:26 -05:00
|
|
|
func (i *ListInputProvider) SetWithExclusions(executionId string, value string) error {
|
2023-11-23 19:35:20 +01:00
|
|
|
URL := strings.TrimSpace(value)
|
|
|
|
|
if URL == "" {
|
2024-03-14 03:08:53 +05:30
|
|
|
return nil
|
2023-11-23 19:35:20 +01:00
|
|
|
}
|
|
|
|
|
if i.isExcluded(URL) {
|
|
|
|
|
i.skippedCount++
|
2024-03-14 03:08:53 +05:30
|
|
|
return nil
|
2023-11-23 19:35:20 +01:00
|
|
|
}
|
2025-07-09 14:47:26 -05:00
|
|
|
i.Set(executionId, URL)
|
2024-03-14 03:08:53 +05:30
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ListInputProvider is a hmap/filekv backed nuclei ListInputProvider provider
|
|
|
|
|
func (i *ListInputProvider) InputType() string {
|
|
|
|
|
return "ListInputProvider"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close closes the input provider
|
|
|
|
|
func (i *ListInputProvider) Close() {
|
2025-07-01 00:40:44 +07:00
|
|
|
_ = i.hostMap.Close()
|
2024-03-14 03:08:53 +05:30
|
|
|
if i.hostMapStream != nil {
|
|
|
|
|
i.hostMapStream.Close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// initializeInputSources initializes the input sources for hmap input
|
|
|
|
|
func (i *ListInputProvider) initializeInputSources(opts *Options) error {
|
|
|
|
|
options := opts.Options
|
|
|
|
|
|
|
|
|
|
// Handle targets flags
|
|
|
|
|
for _, target := range options.Targets {
|
|
|
|
|
switch {
|
|
|
|
|
case iputil.IsCIDR(target):
|
|
|
|
|
ips := expand.CIDR(target)
|
2025-07-09 14:47:26 -05:00
|
|
|
i.addTargets(options.ExecutionId, ips)
|
2024-03-14 03:08:53 +05:30
|
|
|
case asn.IsASN(target):
|
|
|
|
|
ips := expand.ASN(target)
|
2025-07-09 14:47:26 -05:00
|
|
|
i.addTargets(options.ExecutionId, ips)
|
2024-03-14 03:08:53 +05:30
|
|
|
default:
|
2025-07-09 14:47:26 -05:00
|
|
|
i.Set(options.ExecutionId, target)
|
2024-03-14 03:08:53 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle stdin
|
|
|
|
|
if options.Stdin {
|
2025-07-09 14:47:26 -05:00
|
|
|
i.scanInputFromReader(
|
|
|
|
|
options.ExecutionId,
|
|
|
|
|
readerutil.TimeoutReader{Reader: os.Stdin, Timeout: time.Duration(options.InputReadTimeout)})
|
2024-03-14 03:08:53 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle target file
|
|
|
|
|
if options.TargetsFilePath != "" {
|
|
|
|
|
input, inputErr := os.Open(options.TargetsFilePath)
|
|
|
|
|
if inputErr != nil {
|
|
|
|
|
// Handle cloud based input here.
|
|
|
|
|
if opts.NotFoundCallback == nil || !opts.NotFoundCallback(options.TargetsFilePath) {
|
|
|
|
|
return errors.Wrap(inputErr, "could not open targets file")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if input != nil {
|
2025-07-09 14:47:26 -05:00
|
|
|
i.scanInputFromReader(options.ExecutionId, input)
|
2025-07-01 00:40:44 +07:00
|
|
|
_ = input.Close()
|
2024-03-14 03:08:53 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if options.Uncover && options.UncoverQuery != nil {
|
|
|
|
|
gologger.Info().Msgf("Running uncover query against: %s", strings.Join(options.UncoverEngine, ","))
|
|
|
|
|
uncoverOpts := &uncoverlib.Options{
|
|
|
|
|
Agents: options.UncoverEngine,
|
|
|
|
|
Queries: options.UncoverQuery,
|
|
|
|
|
Limit: options.UncoverLimit,
|
|
|
|
|
MaxRetry: options.Retries,
|
|
|
|
|
Timeout: options.Timeout,
|
|
|
|
|
RateLimit: uint(options.UncoverRateLimit),
|
|
|
|
|
RateLimitUnit: time.Minute, // default unit is minute
|
|
|
|
|
}
|
|
|
|
|
ch, err := uncover.GetTargetsFromUncover(context.TODO(), options.UncoverField, uncoverOpts)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for c := range ch {
|
2025-07-09 14:47:26 -05:00
|
|
|
i.Set(options.ExecutionId, c)
|
2024-03-14 03:08:53 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(options.ExcludeTargets) > 0 {
|
|
|
|
|
for _, target := range options.ExcludeTargets {
|
|
|
|
|
switch {
|
|
|
|
|
case iputil.IsCIDR(target):
|
|
|
|
|
ips := expand.CIDR(target)
|
|
|
|
|
i.removeTargets(ips)
|
|
|
|
|
case asn.IsASN(target):
|
|
|
|
|
ips := expand.ASN(target)
|
|
|
|
|
i.removeTargets(ips)
|
|
|
|
|
default:
|
2025-07-09 14:47:26 -05:00
|
|
|
i.Del(options.ExecutionId, target)
|
2024-03-14 03:08:53 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scanInputFromReader scans a line of input from reader and passes it for storage
|
2025-07-09 14:47:26 -05:00
|
|
|
func (i *ListInputProvider) scanInputFromReader(executionId string, reader io.Reader) {
|
2024-03-14 03:08:53 +05:30
|
|
|
scanner := bufio.NewScanner(reader)
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
|
item := scanner.Text()
|
|
|
|
|
switch {
|
|
|
|
|
case iputil.IsCIDR(item):
|
|
|
|
|
ips := expand.CIDR(item)
|
2025-07-09 14:47:26 -05:00
|
|
|
i.addTargets(executionId, ips)
|
2024-03-14 03:08:53 +05:30
|
|
|
case asn.IsASN(item):
|
|
|
|
|
ips := expand.ASN(item)
|
2025-07-09 14:47:26 -05:00
|
|
|
i.addTargets(executionId, ips)
|
2024-03-14 03:08:53 +05:30
|
|
|
default:
|
2025-07-09 14:47:26 -05:00
|
|
|
i.Set(executionId, item)
|
2024-03-14 03:08:53 +05:30
|
|
|
}
|
|
|
|
|
}
|
2023-11-23 19:35:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// isExcluded checks if a URL is in the exclusion list
|
2024-03-14 03:08:53 +05:30
|
|
|
func (i *ListInputProvider) isExcluded(URL string) bool {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
|
|
|
|
metaInput.Input = URL
|
2023-11-23 19:35:20 +01:00
|
|
|
key, err := metaInput.MarshalString()
|
|
|
|
|
if err != nil {
|
|
|
|
|
gologger.Warning().Msgf("%s\n", err)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, exists := i.excludedHosts[key]
|
|
|
|
|
return exists
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-09 14:47:26 -05:00
|
|
|
func (i *ListInputProvider) Del(executionId string, value string) {
|
2023-11-23 19:35:20 +01:00
|
|
|
URL := strings.TrimSpace(value)
|
|
|
|
|
if URL == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// parse hostname if url is given
|
|
|
|
|
urlx, err := urlutil.Parse(URL)
|
|
|
|
|
if err != nil || (urlx != nil && urlx.Host == "") {
|
|
|
|
|
gologger.Debug().Label("url").MsgFunc(func() string {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Sprintf("failed to parse url %v got %v skipping ip selection", URL, err)
|
|
|
|
|
}
|
|
|
|
|
return fmt.Sprintf("got empty hostname for %v skipping ip selection", URL)
|
|
|
|
|
})
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
|
|
|
|
metaInput.Input = URL
|
2023-11-23 19:35:20 +01:00
|
|
|
i.delItem(metaInput)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if input is ip or hostname
|
|
|
|
|
if iputil.IsIP(urlx.Hostname()) {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
|
|
|
|
metaInput.Input = URL
|
2023-11-23 19:35:20 +01:00
|
|
|
i.delItem(metaInput)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if i.ipOptions.ScanAllIPs {
|
|
|
|
|
// scan all ips
|
2025-07-09 14:47:26 -05:00
|
|
|
dialers := protocolstate.GetDialersWithId(executionId)
|
|
|
|
|
if dialers == nil {
|
|
|
|
|
panic("dialers with executionId " + executionId + " not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dnsData, err := dialers.Fastdialer.GetDNSData(urlx.Hostname())
|
2023-11-23 19:35:20 +01:00
|
|
|
if err == nil {
|
|
|
|
|
if (len(dnsData.A) + len(dnsData.AAAA)) > 0 {
|
|
|
|
|
var ips []string
|
|
|
|
|
if i.ipOptions.IPV4 {
|
|
|
|
|
ips = append(ips, dnsData.A...)
|
|
|
|
|
}
|
|
|
|
|
if i.ipOptions.IPV6 {
|
|
|
|
|
ips = append(ips, dnsData.AAAA...)
|
|
|
|
|
}
|
|
|
|
|
for _, ip := range ips {
|
|
|
|
|
if ip == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
|
|
|
|
metaInput.Input = value
|
|
|
|
|
metaInput.CustomIP = ip
|
2023-11-23 19:35:20 +01:00
|
|
|
i.delItem(metaInput)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
} else {
|
|
|
|
|
gologger.Debug().Msgf("scanAllIps: no ip's found reverting to default")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// failed to scanallips falling back to defaults
|
|
|
|
|
gologger.Debug().Msgf("scanAllIps: dns resolution failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ips := []string{}
|
|
|
|
|
// only scan the target but ipv6 if it has one
|
|
|
|
|
if i.ipOptions.IPV6 {
|
2025-07-09 14:47:26 -05:00
|
|
|
dialers := protocolstate.GetDialersWithId(executionId)
|
|
|
|
|
if dialers == nil {
|
|
|
|
|
panic("dialers with executionId " + executionId + " not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dnsData, err := dialers.Fastdialer.GetDNSData(urlx.Hostname())
|
2023-11-23 19:35:20 +01:00
|
|
|
if err == nil && len(dnsData.AAAA) > 0 {
|
|
|
|
|
// pick/ prefer 1st
|
|
|
|
|
ips = append(ips, dnsData.AAAA[0])
|
|
|
|
|
} else {
|
|
|
|
|
gologger.Warning().Msgf("target does not have ipv6 address falling back to ipv4 %v\n", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if i.ipOptions.IPV4 {
|
|
|
|
|
// if IPV4 is enabled do not specify ip let dialer handle it
|
|
|
|
|
ips = append(ips, "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, ip := range ips {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
2023-11-23 19:35:20 +01:00
|
|
|
if ip != "" {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput.Input = URL
|
|
|
|
|
metaInput.CustomIP = ip
|
2023-11-23 19:35:20 +01:00
|
|
|
i.delItem(metaInput)
|
|
|
|
|
} else {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput.Input = URL
|
2023-11-23 19:35:20 +01:00
|
|
|
i.delItem(metaInput)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-09 14:18:56 +01:00
|
|
|
// setItem in the kv store
|
2024-03-14 03:08:53 +05:30
|
|
|
func (i *ListInputProvider) setItem(metaInput *contextargs.MetaInput) {
|
2022-12-05 18:22:04 +05:30
|
|
|
key, err := metaInput.MarshalString()
|
|
|
|
|
if err != nil {
|
|
|
|
|
gologger.Warning().Msgf("%s\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if _, ok := i.hostMap.Get(key); ok {
|
|
|
|
|
i.dupeCount++
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i.inputCount++ // tracks target count
|
|
|
|
|
_ = i.hostMap.Set(key, nil)
|
2021-10-27 15:53:04 +05:30
|
|
|
if i.hostMapStream != nil {
|
2022-12-20 11:07:48 +01:00
|
|
|
i.setHostMapStream(key)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-23 19:35:20 +01:00
|
|
|
// setItem in the kv store
|
2024-03-14 03:08:53 +05:30
|
|
|
func (i *ListInputProvider) delItem(metaInput *contextargs.MetaInput) {
|
2023-11-23 19:35:20 +01:00
|
|
|
targetUrl, err := urlutil.ParseURL(metaInput.Input, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
gologger.Warning().Msgf("%s\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i.hostMap.Scan(func(k, _ []byte) error {
|
|
|
|
|
var tmpMetaInput contextargs.MetaInput
|
|
|
|
|
if err := tmpMetaInput.Unmarshal(string(k)); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
tmpKey, err := tmpMetaInput.MarshalString()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
tmpUrl, err := urlutil.ParseURL(tmpMetaInput.Input, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-28 18:09:39 +03:00
|
|
|
matched, _ := regexp.MatchString(metaInput.Input, tmpUrl.Host)
|
|
|
|
|
if tmpUrl.Host == targetUrl.Host || matched {
|
2023-11-23 19:35:20 +01:00
|
|
|
_ = i.hostMap.Del(tmpKey)
|
|
|
|
|
i.excludedHosts[tmpKey] = struct{}{}
|
|
|
|
|
i.excludedCount++
|
|
|
|
|
i.inputCount--
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-01 14:33:43 -04:00
|
|
|
// setHostMapStream sets item in stream mode
|
2024-03-14 03:08:53 +05:30
|
|
|
func (i *ListInputProvider) setHostMapStream(data string) {
|
2022-12-20 11:07:48 +01:00
|
|
|
if _, err := i.hostMapStream.Merge([][]byte{[]byte(data)}); err != nil {
|
|
|
|
|
gologger.Warning().Msgf("%s\n", err)
|
|
|
|
|
return
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-09 14:47:26 -05:00
|
|
|
func (i *ListInputProvider) addTargets(executionId string, targets []string) {
|
2023-11-23 19:35:20 +01:00
|
|
|
for _, target := range targets {
|
2025-07-09 14:47:26 -05:00
|
|
|
i.Set(executionId, target)
|
2023-11-23 19:35:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 03:08:53 +05:30
|
|
|
func (i *ListInputProvider) removeTargets(targets []string) {
|
2023-11-23 19:35:20 +01:00
|
|
|
for _, target := range targets {
|
2024-07-12 17:23:44 +02:00
|
|
|
metaInput := contextargs.NewMetaInput()
|
|
|
|
|
metaInput.Input = target
|
2023-11-23 19:35:20 +01:00
|
|
|
i.delItem(metaInput)
|
2022-10-14 00:25:00 +05:30
|
|
|
}
|
|
|
|
|
}
|