<commit_before>// Package policy implements Policy Enforcement Point (PEP) for CoreDNS.
// It allows to connect CoreDNS to Policy Decision Point (PDP), send decision
// requests to the PDP and perform actions on DNS requests and replies according
// recieved decisions.
package policy

import (
	"encoding/hex"
	"errors"
	"fmt"
	"log"
	"net"
	"strconv"
	"strings"
	"sync/atomic"
	"time"

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/pkg/trace"
	"github.com/mholt/caddy"
	"github.com/miekg/dns"
	"golang.org/x/net/context"

	pdp "github.com/infobloxopen/themis/pdp-service"
	"github.com/infobloxopen/themis/pep"
)

const (
	typeEDNS0Bytes = iota
	typeEDNS0Hex
	typeEDNS0IP
)

const (
	typeInvalid = iota
	typeRefuse
	typeAllow
	typeRedirect
	typeBlock
	typeLog

	actCount
)

var actionConv [actCount]string

func init() {
	actionConv[typeInvalid] = "invalid"
	actionConv[typeRefuse] = "refuse"
	actionConv[typeAllow] = "allow"
	actionConv[typeRedirect] = "redirect"
	actionConv[typeBlock] = "block"
	actionConv[typeLog] = "log"
}

var (
	errInvalidAction = errors.New("invalid action")
	errInvalidOption = errors.New("invalid policy plugin option")
)

var stringToEDNS0MapType = map[string]uint16{
	"bytes":   typeEDNS0Bytes,
	"hex":     typeEDNS0Hex,
	"address": typeEDNS0IP,
}

type edns0Map struct {
	name     string
	dataType uint16
	destType string
	size     uint
	start    uint
	end      uint
}

// policyPlugin represents a plugin instance that can validate DNS
// requests and replies using PDP server.
type policyPlugin struct {
	endpoints       []string
	options         map[uint16][]*edns0Map
	transfer        map[string]struct{}
	tapIO           dnstapSender
	trace           plugin.Handler
	next            plugin.Handler
	pdp             pep.Client
	debugSuffix     string
	streams         int
	hotSpot         bool
	ident           string
	passthrough     []string
	connTimeout     time.Duration
	connAttempts    map[string]*uint32
	unkConnAttempts *uint32
}

func newPolicyPlugin() *policyPlugin {
	return &policyPlugin{
		options:         make(map[uint16][]*edns0Map),
		transfer:        make(map[string]struct{}),
		connTimeout:     -1,
		connAttempts:    make(map[string]*uint32),
		unkConnAttempts: new(uint32),
	}
}

// connect establishes connection to PDP server.
func (p *policyPlugin) connect() error {
	log.Printf("[DEBUG] Connecting %v", p)

	for _, addr := range p.endpoints {
		p.connAttempts[addr] = new(uint32)
	}

	opts := []pep.Option{
		pep.WithConnectionTimeout(p.connTimeout),
		pep.WithConnectionStateNotification(p.connStateCb),
	}
	if p.streams <= 0 || !p.hotSpot {
		opts = append(opts, pep.WithRoundRobinBalancer(p.endpoints...))
	}

	if p.streams > 0 {
		opts = append(opts, pep.WithStreams(p.streams))
		if p.hotSpot {
			opts = append(opts, pep.WithHotSpotBalancer(p.endpoints...))
		}
	}

	if p.trace != nil {
		if t, ok := p.trace.(trace.Trace); ok {
			opts = append(opts, pep.WithTracer(t.Tracer()))
		}
	}

	p.pdp = pep.NewClient(opts...)
	return p.pdp.Connect("")
}

// closeConn terminates previously established connection.
func (p *policyPlugin) closeConn() {
	if p.pdp != nil {
		p.pdp.Close()
		p.pdp = nil
	}
}

func (p *policyPlugin) parseOption(c *caddy.Controller) error {
	switch c.Val() {
	case "endpoint":
		return p.parseEndpoint(c)

	case "edns0":
		return p.parseEDNS0(c)

	case "debug_query_suffix":
		return p.parseDebugQuerySuffix(c)<commit_msg>Fixed policy plugin panic after graceful restart (nil pointer dereference) (#119)<commit_after>// Package policy implements Policy Enforcement Point (PEP) for CoreDNS.
// It allows to connect CoreDNS to Policy Decision Point (PDP), send decision
// requests to the PDP and perform actions on DNS requests and replies according
// recieved decisions.
package policy

import (
	"encoding/hex"
	"errors"
	"fmt"
	"log"
	"net"
	"strconv"
	"strings"
	"sync/atomic"
	"time"

	"github.com/coredns/coredns/plugin"
	"github.com/coredns/coredns/plugin/pkg/trace"
	"github.com/mholt/caddy"
	"github.com/miekg/dns"
	"golang.org/x/net/context"

	pdp "github.com/infobloxopen/themis/pdp-service"
	"github.com/infobloxopen/themis/pep"
)

const (
	typeEDNS0Bytes = iota
	typeEDNS0Hex
	typeEDNS0IP
)

const (
	typeInvalid = iota
	typeRefuse
	typeAllow
	typeRedirect
	typeBlock
	typeLog

	actCount
)

var actionConv [actCount]string

func init() {
	actionConv[typeInvalid] = "invalid"
	actionConv[typeRefuse] = "refuse"
	actionConv[typeAllow] = "allow"
	actionConv[typeRedirect] = "redirect"
	actionConv[typeBlock] = "block"
	actionConv[typeLog] = "log"
}

var (
	errInvalidAction = errors.New("invalid action")
	errInvalidOption = errors.New("invalid policy plugin option")
)

var stringToEDNS0MapType = map[string]uint16{
	"bytes":   typeEDNS0Bytes,
	"hex":     typeEDNS0Hex,
	"address": typeEDNS0IP,
}

type edns0Map struct {
	name     string
	dataType uint16
	destType string
	size     uint
	start    uint
	end      uint
}

// policyPlugin represents a plugin instance that can validate DNS
// requests and replies using PDP server.
type policyPlugin struct {
	endpoints       []string
	options         map[uint16][]*edns0Map
	transfer        map[string]struct{}
	tapIO           dnstapSender
	trace           plugin.Handler
	next            plugin.Handler
	pdp             pep.Client
	debugSuffix     string
	streams         int
	hotSpot         bool
	ident           string
	passthrough     []string
	connTimeout     time.Duration
	connAttempts    map[string]*uint32
	unkConnAttempts *uint32
}

func newPolicyPlugin() *policyPlugin {
	return &policyPlugin{
		options:         make(map[uint16][]*edns0Map),
		transfer:        make(map[string]struct{}),
		connTimeout:     -1,
		connAttempts:    make(map[string]*uint32),
		unkConnAttempts: new(uint32),
	}
}

// connect establishes connection to PDP server.
func (p *policyPlugin) connect() error {
	log.Printf("[DEBUG] Connecting %v", p)

	for _, addr := range p.endpoints {
		p.connAttempts[addr] = new(uint32)
	}

	opts := []pep.Option{
		pep.WithConnectionTimeout(p.connTimeout),
		pep.WithConnectionStateNotification(p.connStateCb),
	}
	if p.streams <= 0 || !p.hotSpot {
		opts = append(opts, pep.WithRoundRobinBalancer(p.endpoints...))
	}

	if p.streams > 0 {
		opts = append(opts, pep.WithStreams(p.streams))
		if p.hotSpot {
			opts = append(opts, pep.WithHotSpotBalancer(p.endpoints...))
		}
	}

	if p.trace != nil {
		if t, ok := p.trace.(trace.Trace); ok {
			opts = append(opts, pep.WithTracer(t.Tracer()))
		}
	}

	p.pdp = pep.NewClient(opts...)
	return p.pdp.Connect("")
}

// closeConn terminates previously established connection.
func (p *policyPlugin) closeConn() {
	if p.pdp != nil {
		p.pdp.Close()
	}
}

func (p *policyPlugin) parseOption(c *caddy.Controller) error {
	switch c.Val() {
	case "endpoint":
		return p.parseEndpoint(c)

	case "edns0":
		return p.parseEDNS0(c)

	case "debug_query_suffix":
		return p.parseDebugQuerySuffix(c)
{'fraction_non_alphanumeric': 0.11869051052783386, 'fraction_numerical': 0.00995096625324488, 'mean_word_length': 3.614105123087159, 'pattern_counts': {'":': 12, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4100482', 'n_tokens_mistral': 2615, 'n_tokens_neox': 2467, 'n_words': 561}