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"
2021-09-07 17:31:46 +03:00
2020-12-25 02:24:55 +05:30
"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-09-07 17:31:46 +03:00
// ID is the optional 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:
2021-08-26 23:27:42 +05:30
// - "inet"
// - "csnet"
// - "chaos"
// - "hesiod"
// - "none"
// - "any"
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-10-01 16:52:38 +03:00
func ( request * Request ) GetCompiledOperators ( ) [ ] * operators . Operators {
return [ ] * operators . Operators { request . CompiledOperators }
}
2021-01-16 14:10:24 +05:30
// GetID returns the unique ID of the request if any.
2021-10-01 14:30:04 +03:00
func ( request * Request ) GetID ( ) string {
return request . ID
2021-01-16 14:10:24 +05:30
}
2020-12-21 14:31:32 +05:30
// Compile compiles the protocol request for further execution.
2021-10-01 14:30:04 +03:00
func ( request * Request ) Compile ( options * protocols . ExecuterOptions ) error {
2020-12-25 02:24:55 +05:30
// Create a dns client for the class
2020-12-25 12:55:46 +05:30
client , err := dnsclientpool . Get ( options . Options , & dnsclientpool . Configuration {
2021-10-01 14:30:04 +03:00
Retries : request . Retries ,
2020-12-25 02:24:55 +05:30
} )
if err != nil {
return errors . Wrap ( err , "could not get dns client" )
}
2021-10-01 14:30:04 +03:00
request . dnsClient = client
2020-12-25 02:24:55 +05:30
2021-10-01 14:30:04 +03:00
if len ( request . Matchers ) > 0 || len ( request . Extractors ) > 0 {
compiled := & request . Operators
2020-12-30 13:26:55 +05:30
if err := compiled . Compile ( ) ; err != nil {
2020-12-25 02:24:55 +05:30
return errors . Wrap ( err , "could not compile operators" )
}
2021-10-01 14:30:04 +03:00
request . CompiledOperators = compiled
2020-12-25 02:24:55 +05:30
}
2021-10-01 14:30:04 +03:00
request . class = classToInt ( request . Class )
request . options = options
request . question = questionTypeToInt ( request . Type )
2020-12-21 14:31:32 +05:30
return nil
}
// Requests returns the total number of requests the YAML rule will perform
2021-10-01 14:30:04 +03:00
func ( request * Request ) Requests ( ) int {
2020-12-21 14:31:32 +05:30
return 1
}
// Make returns the request to be sent for the protocol
2021-10-01 14:30:04 +03:00
func ( request * Request ) Make ( domain string ) ( * dns . Msg , error ) {
if request . question != dns . TypePTR && net . ParseIP ( domain ) != nil {
2021-02-23 23:16:18 +05:30
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 ( )
2021-10-01 14:30:04 +03:00
req . RecursionDesired = request . Recursion
2020-12-21 14:31:32 +05:30
var q dns . Question
2021-10-01 14:30:04 +03:00
final := replacer . Replace ( request . 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 )
2021-10-01 14:30:04 +03:00
q . Qclass = request . class
q . Qtype = request . question
2020-12-21 14:31:32 +05:30
req . Question = append ( req . Question , q )
2021-09-26 07:22:00 +02:00
req . SetEdns0 ( 4096 , false )
2021-10-01 14:30:04 +03:00
switch request . question {
2021-09-26 07:22:00 +02:00
case dns . TypeTXT :
req . AuthenticatedData = true
}
2020-12-21 14:31:32 +05:30
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
}
2021-09-07 17:31:46 +03:00
// classToInt converts a dns class name to its internal representation
2020-12-21 14:31:32 +05:30
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 )
}