github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/whois/whois.go (about)

     1  package whois
     2  
     3  import (
     4  	"net/url"
     5  	"strings"
     6  	"time"
     7  
     8  	jsoniter "github.com/json-iterator/go"
     9  	"github.com/pkg/errors"
    10  	"github.com/projectdiscovery/rdap"
    11  
    12  	"github.com/projectdiscovery/gologger"
    13  	"github.com/projectdiscovery/nuclei/v2/pkg/operators"
    14  	"github.com/projectdiscovery/nuclei/v2/pkg/operators/extractors"
    15  	"github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers"
    16  	"github.com/projectdiscovery/nuclei/v2/pkg/output"
    17  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
    18  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
    19  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
    20  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/eventcreator"
    21  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/responsehighlighter"
    22  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer"
    23  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/utils/vardump"
    24  	protocolutils "github.com/projectdiscovery/nuclei/v2/pkg/protocols/utils"
    25  	"github.com/projectdiscovery/nuclei/v2/pkg/protocols/whois/rdapclientpool"
    26  	templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
    27  
    28  	"github.com/projectdiscovery/nuclei/v2/pkg/types"
    29  )
    30  
    31  // Request is a request for the WHOIS protocol
    32  type Request struct {
    33  	// Operators for the current request go here.
    34  	operators.Operators `yaml:",inline,omitempty" json:",inline,omitempty"`
    35  	CompiledOperators   *operators.Operators `yaml:"-" json:"-"`
    36  
    37  	// description: |
    38  	//   Query contains query for the request
    39  	Query string `yaml:"query,omitempty" json:"query,omitempty" jsonschema:"title=query for the WHOIS request,description=Query contains query for the request"`
    40  
    41  	// description: |
    42  	// 	 Optional WHOIS server URL.
    43  	//
    44  	// 	 If present, specifies the WHOIS server to execute the Request on.
    45  	//   Otherwise, nil enables bootstrapping
    46  	Server string `yaml:"server,omitempty" json:"server,omitempty" jsonschema:"title=server url to execute the WHOIS request on,description=Server contains the server url to execute the WHOIS request on"`
    47  	// cache any variables that may be needed for operation.
    48  	client          *rdap.Client
    49  	options         *protocols.ExecutorOptions
    50  	parsedServerURL *url.URL
    51  }
    52  
    53  // Compile compiles the request generators preparing any requests possible.
    54  func (request *Request) Compile(options *protocols.ExecutorOptions) error {
    55  	var err error
    56  	if request.Server != "" {
    57  		request.parsedServerURL, err = url.Parse(request.Server)
    58  		if err != nil {
    59  			return errors.Wrap(err, "failed to parse server URL")
    60  		}
    61  	}
    62  
    63  	request.options = options
    64  	request.client, _ = rdapclientpool.Get(options.Options, nil)
    65  
    66  	if len(request.Matchers) > 0 || len(request.Extractors) > 0 {
    67  		compiled := &request.Operators
    68  		compiled.ExcludeMatchers = options.ExcludeMatchers
    69  		compiled.TemplateID = options.TemplateID
    70  		if err := compiled.Compile(); err != nil {
    71  			return errors.Wrap(err, "could not compile operators")
    72  		}
    73  		request.CompiledOperators = compiled
    74  	}
    75  	return nil
    76  }
    77  
    78  // Requests returns the total number of requests the rule will perform
    79  func (request *Request) Requests() int {
    80  	return 1
    81  }
    82  
    83  // GetID returns the ID for the request if any.
    84  func (request *Request) GetID() string {
    85  	return ""
    86  }
    87  
    88  // ExecuteWithResults executes the protocol requests and returns results instead of writing them.
    89  func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
    90  	// generate variables
    91  	defaultVars := protocolutils.GenerateVariables(input.MetaInput.Input, false, nil)
    92  	optionVars := generators.BuildPayloadFromOptions(request.options.Options)
    93  	vars := request.options.Variables.Evaluate(generators.MergeMaps(defaultVars, optionVars, dynamicValues))
    94  
    95  	variables := generators.MergeMaps(vars, defaultVars, optionVars, dynamicValues, request.options.Constants)
    96  
    97  	if vardump.EnableVarDump {
    98  		gologger.Debug().Msgf("Protocol request variables: \n%s\n", vardump.DumpVariables(variables))
    99  	}
   100  
   101  	// and replace placeholders
   102  	query := replacer.Replace(request.Query, variables)
   103  	// build an rdap request
   104  	rdapReq := rdap.NewAutoRequest(query)
   105  	rdapReq.Server = request.parsedServerURL
   106  	res, err := request.client.Do(rdapReq)
   107  	if err != nil {
   108  		return errors.Wrap(err, "could not make whois request")
   109  	}
   110  	gologger.Verbose().Msgf("Sent WHOIS request to %s", query)
   111  	if request.options.Options.Debug || request.options.Options.DebugRequests {
   112  		gologger.Debug().Msgf("[%s] Dumped WHOIS request for %s", request.options.TemplateID, query)
   113  	}
   114  
   115  	data := make(map[string]interface{})
   116  	var response interface{}
   117  	switch rdapReq.Type {
   118  	case rdap.DomainRequest:
   119  		// convert the rdap response to a whois style response (for domain request type only)
   120  		whoisResp := res.ToWhoisStyleResponse()
   121  		for k, v := range whoisResp.Data {
   122  			data[strings.ToLower(k)] = strings.Join(v, ",")
   123  		}
   124  		response = whoisResp
   125  	default:
   126  		response = res.Object
   127  	}
   128  	jsonData, _ := jsoniter.Marshal(response)
   129  	jsonDataString := string(jsonData)
   130  
   131  	data["type"] = request.Type().String()
   132  	data["host"] = query
   133  	data["response"] = jsonDataString
   134  
   135  	event := eventcreator.CreateEvent(request, data, request.options.Options.Debug || request.options.Options.DebugResponse)
   136  	if request.options.Options.Debug || request.options.Options.DebugResponse {
   137  		gologger.Debug().Msgf("[%s] Dumped WHOIS response for %s", request.options.TemplateID, query)
   138  		gologger.Print().Msgf("%s", responsehighlighter.Highlight(event.OperatorsResult, jsonDataString, request.options.Options.NoColor, false))
   139  	}
   140  
   141  	callback(event)
   142  	return nil
   143  }
   144  
   145  // Match performs matching operation for a matcher on model and returns:
   146  // true and a list of matched snippets if the matcher type is supports it
   147  // otherwise false and an empty string slice
   148  func (request *Request) Match(data map[string]interface{}, matcher *matchers.Matcher) (bool, []string) {
   149  	return protocols.MakeDefaultMatchFunc(data, matcher)
   150  }
   151  
   152  // Extract performs extracting operation for an extractor on model and returns true or false.
   153  func (request *Request) Extract(data map[string]interface{}, matcher *extractors.Extractor) map[string]struct{} {
   154  	return protocols.MakeDefaultExtractFunc(data, matcher)
   155  }
   156  
   157  // MakeResultEvent creates a result event from internal wrapped event
   158  func (request *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*output.ResultEvent {
   159  	return protocols.MakeDefaultResultEvent(request, wrapped)
   160  }
   161  
   162  // GetCompiledOperators returns a list of the compiled operators
   163  func (request *Request) GetCompiledOperators() []*operators.Operators {
   164  	return []*operators.Operators{request.CompiledOperators}
   165  }
   166  
   167  func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
   168  	data := &output.ResultEvent{
   169  		TemplateID:       types.ToString(request.options.TemplateID),
   170  		TemplatePath:     types.ToString(request.options.TemplatePath),
   171  		Info:             request.options.TemplateInfo,
   172  		Type:             types.ToString(wrapped.InternalEvent["type"]),
   173  		Host:             types.ToString(wrapped.InternalEvent["host"]),
   174  		Metadata:         wrapped.OperatorsResult.PayloadValues,
   175  		ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
   176  		Timestamp:        time.Now(),
   177  		MatcherStatus:    true,
   178  		Request:          types.ToString(wrapped.InternalEvent["request"]),
   179  		Response:         types.ToString(wrapped.InternalEvent["response"]),
   180  	}
   181  	return data
   182  }
   183  
   184  // Type returns the type of the protocol request
   185  func (request *Request) Type() templateTypes.ProtocolType {
   186  	return templateTypes.WHOISProtocol
   187  }