2020-12-21 14:31:32 +05:30
package dns
import (
2021-02-23 23:16:18 +05:30
"net"
2020-12-21 14:31:32 +05:30
"strings"
"github.com/miekg/dns"
2020-12-25 02:24:55 +05:30
"github.com/pkg/errors"
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
2020-12-24 12:56:28 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer"
2020-12-25 12:55:46 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/dns/dnsclientpool"
2020-12-25 02:24:55 +05:30
"github.com/projectdiscovery/retryabledns"
2020-12-21 14:31:32 +05:30
)
// Request contains a DNS protocol request to be made from a template
type Request struct {
2021-02-26 13:13:11 +05:30
// Operators for the current request go here.
operators . Operators ` yaml:",inline" `
2021-08-03 22:33:50 +05:30
// ID is the ID of the request
2021-08-23 23:50:45 +05:30
ID string ` yaml:"id,omitempty" jsonschema:"title=id of the dns request,description=ID is the optional ID of the DNS Request" `
2021-01-16 14:10:24 +05:30
2021-07-27 16:03:56 +05:30
// description: |
// Name is the Hostname to make DNS request for.
//
// Generally, it is set to {{FQDN}} which is the domain we get from input.
// examples:
// - value: "\"{{FQDN}}\""
2021-08-23 23:50:45 +05:30
Name string ` yaml:"name,omitempty" jsonschema:"title=hostname to make dns request for,description=Name is the Hostname to make DNS request for" `
2021-07-27 16:03:56 +05:30
// description: |
// Type is the type of DNS request to make.
// values:
// - "A"
// - "NS"
2021-08-23 23:50:45 +05:30
// - "DS"
2021-07-27 16:03:56 +05:30
// - "CNAME"
// - "SOA"
// - "PTR"
// - "MX"
// - "TXT"
// - "AAAA"
2021-08-23 23:50:45 +05:30
Type string ` yaml:"type,omitempty" jsonschema:"title=type of dns request to make,description=Type is the type of DNS request to make,enum=A,enum=NS,enum=DS,enum=CNAME,enum=SOA,enum=PTR,enum=MX,enum=TXT,enum=AAAA" `
2021-07-27 16:03:56 +05:30
// description: |
// Class is the class of the DNS request.
//
// Usually it's enough to just leave it as INET.
// values:
// - "INET"
// - "CSNET"
// - "CHAOS"
// - "HESIOD"
// - "NONE"
// - "ANY"
2021-08-23 23:50:45 +05:30
Class string ` yaml:"class,omitempty" jsonschema:"title=class of DNS request,description=Class is the class of the DNS request,enum=INET,enum=CSNET,enum=CHAOS,enum=HESIOD,enum=NONE,enum=ANY" `
2021-07-27 16:03:56 +05:30
// description: |
// Retries is the number of retries for the DNS request
// examples:
// - name: Use a retry of 3 to 5 generally
// value: 5
2021-08-23 23:50:45 +05:30
Retries int ` yaml:"retries,omitempty" jsonschema:"title=retries for dns request,description=Retries is the number of retries for the DNS request" `
2020-12-21 14:31:32 +05:30
2021-08-04 14:20:48 +05:30
CompiledOperators * operators . Operators ` yaml:"-" `
2021-02-26 13:13:11 +05:30
dnsClient * retryabledns . Client
options * protocols . ExecuterOptions
2020-12-25 02:24:55 +05:30
2020-12-21 14:31:32 +05:30
// cache any variables that may be needed for operation.
2021-02-26 13:13:11 +05:30
class uint16
question uint16
2021-07-27 16:03:56 +05:30
// description: |
// Recursion determines if resolver should recurse all records to get fresh results.
2021-08-23 23:50:45 +05:30
Recursion bool ` yaml:"recursion,omitempty" jsonschema:"title=recurse all servers,description=Recursion determines if resolver should recurse all records to get fresh results" `
2020-12-21 14:31:32 +05:30
}
2021-01-16 14:10:24 +05:30
// GetID returns the unique ID of the request if any.
func ( r * Request ) GetID ( ) string {
return r . ID
}
2020-12-21 14:31:32 +05:30
// Compile compiles the protocol request for further execution.
2020-12-25 02:24:55 +05:30
func ( r * Request ) Compile ( options * protocols . ExecuterOptions ) error {
// Create a dns client for the class
2020-12-25 12:55:46 +05:30
client , err := dnsclientpool . Get ( options . Options , & dnsclientpool . Configuration {
2020-12-25 02:24:55 +05:30
Retries : r . Retries ,
} )
if err != nil {
return errors . Wrap ( err , "could not get dns client" )
}
r . dnsClient = client
2020-12-30 13:26:55 +05:30
if len ( r . Matchers ) > 0 || len ( r . Extractors ) > 0 {
compiled := & r . Operators
if err := compiled . Compile ( ) ; err != nil {
2020-12-25 02:24:55 +05:30
return errors . Wrap ( err , "could not compile operators" )
}
2020-12-30 13:26:55 +05:30
r . CompiledOperators = compiled
2020-12-25 02:24:55 +05:30
}
2020-12-21 14:31:32 +05:30
r . class = classToInt ( r . Class )
2020-12-25 02:24:55 +05:30
r . options = options
r . question = questionTypeToInt ( r . Type )
2020-12-21 14:31:32 +05:30
return nil
}
// Requests returns the total number of requests the YAML rule will perform
2020-12-29 15:38:14 +05:30
func ( r * Request ) Requests ( ) int {
2020-12-21 14:31:32 +05:30
return 1
}
// Make returns the request to be sent for the protocol
func ( r * Request ) Make ( domain string ) ( * dns . Msg , error ) {
2021-02-23 23:16:18 +05:30
if r . question != dns . TypePTR && net . ParseIP ( domain ) != nil {
return nil , errors . New ( "cannot use IP address as DNS input" )
}
2020-12-21 14:31:32 +05:30
domain = dns . Fqdn ( domain )
// Build a request on the specified URL
req := new ( dns . Msg )
req . Id = dns . Id ( )
req . RecursionDesired = r . Recursion
var q dns . Question
2021-02-04 04:48:45 +05:30
final := replacer . Replace ( r . Name , map [ string ] interface { } { "FQDN" : domain } )
2020-12-21 14:31:32 +05:30
2021-02-04 04:48:45 +05:30
q . Name = dns . Fqdn ( final )
2020-12-25 02:24:55 +05:30
q . Qclass = r . class
q . Qtype = r . question
2020-12-21 14:31:32 +05:30
req . Question = append ( req . Question , q )
return req , nil
}
// questionTypeToInt converts DNS question type to internal representation
2021-02-26 13:13:11 +05:30
func questionTypeToInt ( questionType string ) uint16 {
questionType = strings . TrimSpace ( strings . ToUpper ( questionType ) )
2020-12-21 14:31:32 +05:30
question := dns . TypeA
2021-02-26 13:13:11 +05:30
switch questionType {
2020-12-21 14:31:32 +05:30
case "A" :
question = dns . TypeA
case "NS" :
question = dns . TypeNS
case "CNAME" :
question = dns . TypeCNAME
case "SOA" :
question = dns . TypeSOA
case "PTR" :
question = dns . TypePTR
case "MX" :
question = dns . TypeMX
case "TXT" :
question = dns . TypeTXT
2021-07-21 16:48:01 +02:00
case "DS" :
question = dns . TypeDS
2020-12-21 14:31:32 +05:30
case "AAAA" :
question = dns . TypeAAAA
}
2021-02-26 13:13:11 +05:30
return question
2020-12-21 14:31:32 +05:30
}
// classToInt converts a dns class name to it's internal representation
func classToInt ( class string ) uint16 {
class = strings . TrimSpace ( strings . ToUpper ( class ) )
result := dns . ClassINET
switch class {
case "INET" :
result = dns . ClassINET
case "CSNET" :
result = dns . ClassCSNET
case "CHAOS" :
result = dns . ClassCHAOS
case "HESIOD" :
result = dns . ClassHESIOD
case "NONE" :
result = dns . ClassNONE
case "ANY" :
result = dns . ClassANY
}
return uint16 ( result )
}