mirror of
https://github.com/Li4n0/revsuit.git
synced 2026-09-21 22:30:46 +08:00
Support http,dns and mysql connection. Support custom http, dns response. Support dns rebinding. Support mysql load local files and jdbc deserialize exploit.
264 lines
6.0 KiB
Go
264 lines
6.0 KiB
Go
package newdns
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Zone describes a single authoritative DNS zone.
|
|
type Zone struct {
|
|
// The FQDN of the zone e.g. "example.com.".
|
|
Name string
|
|
|
|
// The FQDN of the master mame server responsible for this zone. The FQDN
|
|
// must be returned as A and AAAA record by the parent zone.
|
|
MasterNameServer string
|
|
|
|
// A list of FQDNs to all authoritative name servers for this zone. The
|
|
// FQDNs must be returned as A and AAAA records by the parent zone. It is
|
|
// required to announce at least two distinct name servers per zone.
|
|
AllNameServers []string
|
|
|
|
// The email address of the administrator e.g. "[email protected]".
|
|
//
|
|
// Default: "hostmaster@NAME".
|
|
AdminEmail string
|
|
|
|
// The refresh interval.
|
|
//
|
|
// Default: 6h.
|
|
Refresh time.Duration
|
|
|
|
// The retry interval for the zone.
|
|
//
|
|
// Default: 1h.
|
|
Retry time.Duration
|
|
|
|
// The expiration interval of the zone.
|
|
//
|
|
// Default: 72h.
|
|
Expire time.Duration
|
|
|
|
// The TTL for the SOA record.
|
|
//
|
|
// Default: 15m.
|
|
SOATTL time.Duration
|
|
|
|
// The TTL for NS records.
|
|
//
|
|
// Default: 48h.
|
|
NSTTL time.Duration
|
|
|
|
// The minimum TTL for all records. Either this value, or the SOATTL if lower,
|
|
// is used to determine the "negative caching TTL" which is the duration
|
|
// caches are allowed to cache missing records (NXDOMAIN).
|
|
//
|
|
// Default: 5min.
|
|
MinTTL time.Duration
|
|
|
|
// The handler that responds to requests for this zone. The returned sets
|
|
// must not be altered going forward.
|
|
Handler func(name, remoteAddr string) ([]Set, error)
|
|
}
|
|
|
|
// Validate will validate the zone and ensure the documented defaults.
|
|
func (z *Zone) Validate() error {
|
|
// check name
|
|
if !IsDomain(z.Name, true) {
|
|
return errors.Errorf("name not fully qualified: %s", z.Name)
|
|
}
|
|
|
|
// check master name server
|
|
if !IsDomain(z.MasterNameServer, true) {
|
|
return errors.Errorf("master server not full qualified: %s", z.MasterNameServer)
|
|
}
|
|
|
|
// check name server count
|
|
if len(z.AllNameServers) < 1 {
|
|
return errors.Errorf("missing name servers")
|
|
}
|
|
|
|
// check name servers
|
|
var includesMaster bool
|
|
for _, ns := range z.AllNameServers {
|
|
if !IsDomain(ns, true) {
|
|
return errors.Errorf("name server not fully qualified: %s", ns)
|
|
}
|
|
|
|
if ns == z.MasterNameServer {
|
|
includesMaster = true
|
|
}
|
|
}
|
|
|
|
// check master inclusion
|
|
if !includesMaster {
|
|
return errors.Errorf("master name server not listed as name server: %s", z.MasterNameServer)
|
|
}
|
|
|
|
// set default admin email
|
|
if z.AdminEmail == "" {
|
|
z.AdminEmail = fmt.Sprintf("hostmaster@%s", z.Name)
|
|
}
|
|
|
|
// check admin email
|
|
if !IsDomain(emailToDomain(z.AdminEmail), true) {
|
|
return errors.Errorf("admin email cannot be converted to a domain name: %s", z.AdminEmail)
|
|
}
|
|
|
|
// set default refresh
|
|
if z.Refresh == 0 {
|
|
z.Refresh = 6 * time.Hour
|
|
}
|
|
|
|
// set default retry
|
|
if z.Retry == 0 {
|
|
z.Retry = time.Hour
|
|
}
|
|
|
|
// set default expire
|
|
if z.Expire == 0 {
|
|
z.Expire = 72 * time.Hour
|
|
}
|
|
|
|
// set default SOA TTL
|
|
if z.SOATTL == 0 {
|
|
z.SOATTL = 15 * time.Minute
|
|
}
|
|
|
|
// set default NS TTL
|
|
if z.NSTTL == 0 {
|
|
z.NSTTL = 48 * time.Hour
|
|
}
|
|
|
|
// set default min TTL
|
|
if z.MinTTL == 0 {
|
|
z.MinTTL = 5 * time.Minute
|
|
}
|
|
|
|
// check retry
|
|
if z.Retry >= z.Refresh {
|
|
return errors.Errorf("retry must be less than refresh: %d", z.Retry)
|
|
}
|
|
|
|
// check expire
|
|
if z.Expire < z.Refresh+z.Retry {
|
|
return errors.Errorf("expire must be bigger than the sum of refresh and retry: %d", z.Expire)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Lookup will lookup the specified name in the zone and return results for the
|
|
// specified record types. If no results are returned, the second return value
|
|
// indicates if there are other results for the specified name.
|
|
func (z *Zone) Lookup(name, remoteAddr string, needle ...Type) ([]Set, bool, error) {
|
|
// check name
|
|
if !IsDomain(name, true) {
|
|
return nil, false, errors.Errorf("invalid name: %s", name)
|
|
}
|
|
|
|
// normalize name
|
|
name = NormalizeDomain(name, true, false, false)
|
|
|
|
// check name
|
|
if !InZone(z.Name, name) {
|
|
return nil, false, errors.Errorf("name does not belong to zone: %s", name)
|
|
}
|
|
|
|
// prepare result
|
|
var result []Set
|
|
|
|
for i := 0; ; i++ {
|
|
// get sets
|
|
sets, err := z.Handler(TrimZone(z.Name, name),remoteAddr)
|
|
if err != nil {
|
|
return nil, false, errors.Wrap(err, "zone handler error")
|
|
}
|
|
|
|
// return immediately if initial set is empty
|
|
if i == 0 && len(sets) == 0 {
|
|
return nil, false, nil
|
|
}
|
|
|
|
// prepare counters
|
|
counters := map[Type]int{
|
|
A: 0,
|
|
AAAA: 0,
|
|
CNAME: 0,
|
|
MX: 0,
|
|
TXT: 0,
|
|
}
|
|
|
|
// validate sets
|
|
for _, set := range sets {
|
|
// validate set
|
|
err = set.Validate()
|
|
if err != nil {
|
|
return nil, false, errors.Wrap(err, "invalid set")
|
|
}
|
|
|
|
// check relationship
|
|
if !InZone(z.Name, set.Name) {
|
|
return nil, false, errors.Errorf("set does not belong to zone: %s", set.Name)
|
|
}
|
|
|
|
// increment counter
|
|
counters[set.Type]++
|
|
}
|
|
|
|
// check counters
|
|
for _, counter := range counters {
|
|
if counter > 1 {
|
|
return nil, false, errors.New("multiple sets for same type")
|
|
}
|
|
}
|
|
|
|
// check apex CNAME
|
|
if counters[CNAME] > 0 && name == z.Name {
|
|
return nil, false, errors.Errorf("invalid CNAME set at apex: %s", name)
|
|
}
|
|
|
|
// check CNAME is stand-alone
|
|
if counters[CNAME] > 0 && (len(sets) > 1) {
|
|
return nil, false, errors.Errorf("other sets with CNAME set: %s", name)
|
|
}
|
|
|
|
// check if CNAME and query is not CNAME
|
|
if counters[CNAME] > 0 && !typeInList(needle, CNAME) {
|
|
// add set to result
|
|
result = append(result, sets[0])
|
|
|
|
// get normalized address
|
|
address := NormalizeDomain(sets[0].Records[0].Address, true, false, false)
|
|
|
|
// continue lookup with CNAME address if address is in zone
|
|
if InZone(z.Name, address) {
|
|
name = address
|
|
continue
|
|
}
|
|
|
|
return result, false, nil
|
|
}
|
|
|
|
// add matching set
|
|
for _, set := range sets {
|
|
if typeInList(needle, set.Type) {
|
|
// add set to result
|
|
result = append(result, set)
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
// return if there are not matches, but indicate that there are sets
|
|
// available for other types
|
|
if len(result) == 0 {
|
|
return nil, true, nil
|
|
}
|
|
|
|
return result, false, nil
|
|
}
|
|
}
|