180 lines
4.4 KiB
Go
Raw Normal View History

package dns
import (
2021-02-23 23:16:18 +05:30
"net"
"strings"
"github.com/miekg/dns"
"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"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/dns/dnsclientpool"
"github.com/projectdiscovery/retryabledns"
)
// 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-01-16 14:10:24 +05:30
ID string `yaml:"id"`
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}}\""
Name string `yaml:"name"`
2021-07-27 16:03:56 +05:30
// description: |
// Type is the type of DNS request to make.
// values:
// - "A"
// - "NS"
// - "CNAME"
// - "SOA"
// - "PTR"
// - "MX"
// - "TXT"
// - "AAAA"
Type string `yaml:"type"`
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"
Class string `yaml:"class"`
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
Retries int `yaml:"retries"`
2021-02-26 13:13:11 +05:30
CompiledOperators *operators.Operators
dnsClient *retryabledns.Client
options *protocols.ExecuterOptions
// 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-02-26 13:13:11 +05:30
Recursion bool `yaml:"recursion"`
}
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
}
// Compile compiles the protocol request for further execution.
func (r *Request) Compile(options *protocols.ExecuterOptions) error {
// Create a dns client for the class
client, err := dnsclientpool.Get(options.Options, &dnsclientpool.Configuration{
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 {
return errors.Wrap(err, "could not compile operators")
}
2020-12-30 13:26:55 +05:30
r.CompiledOperators = compiled
}
r.class = classToInt(r.Class)
r.options = options
r.question = questionTypeToInt(r.Type)
return nil
}
// Requests returns the total number of requests the YAML rule will perform
func (r *Request) Requests() int {
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")
}
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
final := replacer.Replace(r.Name, map[string]interface{}{"FQDN": domain})
q.Name = dns.Fqdn(final)
q.Qclass = r.class
q.Qtype = r.question
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))
question := dns.TypeA
2021-02-26 13:13:11 +05:30
switch questionType {
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
case "DS":
question = dns.TypeDS
case "AAAA":
question = dns.TypeAAAA
}
2021-02-26 13:13:11 +05:30
return question
}
// 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)
}