func (rrl *RRL) addrPrefix(addr string) string {
	i := strings.LastIndex(addr, ":")
	ip := net.ParseIP(addr[:i])
	if ip.To4() != nil {
		ip = ip.Mask(net.CIDRMask(rrl.ipv4PrefixLength, 32))
		return ip.String()
	}
	ip = net.ParseIP(addr[1 : i-1])  
	ip = ip.Mask(net.CIDRMask(rrl.ipv6PrefixLength, 128))
	return ip.String()
}
addrPrefix returns the address prefix of the net.Addr style address string (e.g. 1.2.3.4:1234 or [1:2::3:4]:1234)
package rrl
import (
	"errors"
	"net"
	"strconv"
	"strings"
	"time"
	"github.com/coredns/rrl/plugins/rrl/cache"
	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/pkg/nonwriter"
	"github.com/miekg/dns"
)
type RRL struct {
	Next  plugin.Handler
	Zones []string
	window int64
	ipv4PrefixLength int
	ipv6PrefixLength int
	responsesInterval int64
	nodataInterval    int64
	nxdomainsInterval int64
	referralsInterval int64
	errorsInterval    int64
	requestsInterval int64
	reportOnly bool
	maxTableSize int
	table *cache.Cache
}
type ResponseAccount struct {
	allowTime int64
}
const (
	rTypeResponse = 0
	rTypeNodata   = 1
	rTypeNxdomain = 2
	rTypeReferral = 3
	rTypeError    = 4
)
func responseType(m *dns.Msg) byte {
	if len(m.Answer) > 0 {
		return rTypeResponse
	} else if m.Rcode == dns.RcodeNameError {
		return rTypeNxdomain
	} else if m.Rcode == dns.RcodeSuccess {
		if len(m.Ns) > 0 && m.Ns[0].Header().Rrtype == dns.TypeNS {
			return rTypeReferral
		}
		return rTypeNodata
	} else {
		return rTypeError
	}
}
func (rrl *RRL) allowanceForRtype(rtype uint8) int64 {
	switch rtype {
	case rTypeResponse:
		return rrl.responsesInterval
	case rTypeNodata:
		return rrl.nodataInterval
	case rTypeNxdomain:
		return rrl.nxdomainsInterval
	case rTypeReferral:
		return rrl.referralsInterval
	case rTypeError:
		return rrl.errorsInterval
	}
	return -1
}
func (rrl *RRL) initTable() {
	rrl.table = cache.New(rrl.maxTableSize)
	rrl.table.SetEvict(func(el *interface{}) bool {
		ra, ok := (*el).(ResponseAccount)
		if !ok {
			return true
		}
		return time.Now().UnixNano()-ra.allowTime >= rrl.window
	})
}
func (rrl *RRL) responseToToken(nw *nonwriter.Writer, rtype byte) string {
	var name string
	if rtype == rTypeNxdomain || rtype == rTypeReferral {
		if len(nw.Msg.Ns) > 0 {
			name = nw.Msg.Ns[0].Header().Name
		}
	} else {
		name = nw.Msg.Question[0].Name
	}
	return rrl.buildToken(rtype, nw.Msg.Question[0].Qtype, name, nw.RemoteAddr().String())
}
func (rrl *RRL) buildToken(rtype uint8, qtype uint16, name, remoteAddr string) string {
	prefix := rrl.addrPrefix(remoteAddr)
	rtypestr := strconv.FormatUint(uint64(rtype), 10)
	switch rtype {
	case rTypeResponse:
		qtypeStr := strconv.FormatUint(uint64(qtype), 10)
		return strings.Join([]string{prefix, rtypestr, qtypeStr, name}, "/")
	case rTypeNodata:
		return strings.Join([]string{prefix, rtypestr, "", name}, "/")
	case rTypeNxdomain:
		return strings.Join([]string{prefix, rtypestr, "", name}, "/")
	case rTypeReferral:
		qtypeStr := strconv.FormatUint(uint64(qtype), 10)
		return strings.Join([]string{prefix, rtypestr, qtypeStr, name}, "/")
	case rTypeError:
		return strings.Join([]string{prefix, rtypestr, "", ""}, "/")
	}
	return ""
}
func (rrl *RRL) debit(allowance int64, t string) (int64, error) {
	result := rrl.table.UpdateAdd(t,
		func(el *interface{}) interface{} {
			ra := (*el).(*ResponseAccount)
			if ra == nil {
				return nil
			}
			now := time.Now().UnixNano()
			balance := now - ra.allowTime - allowance
			if balance >= second {
				balance = second - allowance
			} else if balance < -rrl.window {
				balance = -rrl.window
			}
			ra.allowTime = now - balance
			return balance
		},
		func() interface{} {
			ra := &ResponseAccount{
				allowTime: time.Now().UnixNano() - second + allowance,
			}
			return ra
		})
	if result == nil {
		return 0, nil
	}
	if err, ok := result.(error); ok {
		return 0, err
	}
	if balance, ok := result.(int64); ok {
		return balance, nil
	}
	return 0, errors.New("unexpected result type")
}