Add option key responsible for the verdict

Also, expose the RevisionCounter
This commit is contained in:
Daniel 2020-10-29 16:24:17 +01:00
parent 263eb0578a
commit c09d32cf08
8 changed files with 176 additions and 133 deletions

View file

@ -17,13 +17,14 @@ import (
"github.com/safing/portmaster/resolver"
)
func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) ([]dns.RR, []string, int) {
func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) ([]dns.RR, []string, int, string) {
goodEntries := make([]dns.RR, 0, len(entries))
filteredRecords := make([]string, 0, len(entries))
// keeps track of the number of valid and allowed
// A and AAAA records.
var allowedAddressRecords int
var interveningOptionKey string
for _, rr := range entries {
// get IP and classification
@ -45,10 +46,12 @@ func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) (
case classification == netutils.HostLocal:
// No DNS should return localhost addresses
filteredRecords = append(filteredRecords, rr.String())
interveningOptionKey = profile.CfgOptionRemoveOutOfScopeDNSKey
continue
case scope == netutils.Global && (classification == netutils.SiteLocal || classification == netutils.LinkLocal):
// No global DNS should return LAN addresses
filteredRecords = append(filteredRecords, rr.String())
interveningOptionKey = profile.CfgOptionRemoveOutOfScopeDNSKey
continue
}
}
@ -58,12 +61,15 @@ func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) (
switch {
case p.BlockScopeInternet() && classification == netutils.Global:
filteredRecords = append(filteredRecords, rr.String())
interveningOptionKey = profile.CfgOptionBlockScopeInternetKey
continue
case p.BlockScopeLAN() && (classification == netutils.SiteLocal || classification == netutils.LinkLocal):
filteredRecords = append(filteredRecords, rr.String())
interveningOptionKey = profile.CfgOptionBlockScopeLANKey
continue
case p.BlockScopeLocal() && classification == netutils.HostLocal:
filteredRecords = append(filteredRecords, rr.String())
interveningOptionKey = profile.CfgOptionBlockScopeLocalKey
continue
}
@ -75,7 +81,7 @@ func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) (
goodEntries = append(goodEntries, rr)
}
return goodEntries, filteredRecords, allowedAddressRecords
return goodEntries, filteredRecords, allowedAddressRecords, interveningOptionKey
}
func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *resolver.RRCache {
@ -97,18 +103,19 @@ func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *res
var filteredRecords []string
var validIPs int
var interveningOptionKey string
rrCache.Answer, filteredRecords, validIPs = filterDNSSection(rrCache.Answer, p, rrCache.ServerScope)
rrCache.Answer, filteredRecords, validIPs, interveningOptionKey = filterDNSSection(rrCache.Answer, p, rrCache.ServerScope)
rrCache.FilteredEntries = append(rrCache.FilteredEntries, filteredRecords...)
// we don't count the valid IPs in the extra section
rrCache.Extra, filteredRecords, _ = filterDNSSection(rrCache.Extra, p, rrCache.ServerScope)
rrCache.Extra, filteredRecords, _, _ = filterDNSSection(rrCache.Extra, p, rrCache.ServerScope)
rrCache.FilteredEntries = append(rrCache.FilteredEntries, filteredRecords...)
if len(rrCache.FilteredEntries) > 0 {
rrCache.Filtered = true
if validIPs == 0 {
conn.Block("no addresses returned for this domain are permitted")
conn.Block("no addresses returned for this domain are permitted", interveningOptionKey)
// If all entries are filtered, this could mean that these are broken/bogus resource records.
if rrCache.Expired() {
@ -151,12 +158,6 @@ func DecideOnResolvedDNS(
rrCache *resolver.RRCache,
) *resolver.RRCache {
// check profile
if checkProfileExists(ctx, conn, nil) {
// returns true if check triggered
return nil
}
// special grant for connectivity domains
if checkConnectivityDomain(ctx, conn, nil) {
// returns true if check triggered
@ -186,14 +187,14 @@ func mayBlockCNAMEs(ctx context.Context, conn *network.Connection) bool {
result, reason := conn.Process().Profile().MatchEndpoint(ctx, conn.Entity)
if result == endpoints.Denied {
conn.BlockWithContext(reason.String(), reason.Context())
conn.BlockWithContext(reason.String(), profile.CfgOptionFilterCNAMEKey, reason.Context())
return true
}
if result == endpoints.NoMatch {
result, reason = conn.Process().Profile().MatchFilterLists(ctx, conn.Entity)
if result == endpoints.Denied {
conn.BlockWithContext(reason.String(), reason.Context())
conn.BlockWithContext(reason.String(), profile.CfgOptionFilterCNAMEKey, reason.Context())
return true
}
}

View file

@ -85,11 +85,11 @@ func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict
verdict = network.VerdictDrop
continueInspection = true
case BLOCK_CONN:
conn.SetVerdict(network.VerdictBlock, "", nil)
conn.SetVerdict(network.VerdictBlock, "", "", nil)
verdict = conn.Verdict
activeInspectors[key] = true
case DROP_CONN:
conn.SetVerdict(network.VerdictDrop, "", nil)
conn.SetVerdict(network.VerdictDrop, "", "", nil)
verdict = conn.Verdict
activeInspectors[key] = true
case STOP_INSPECTING:

View file

@ -171,7 +171,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
ps := getPortStatusAndMarkUsed(pkt.Info().LocalPort())
if ps.isMe {
// approve
conn.Accept("internally approved")
conn.Accept("connection by Portmaster", noReasonOptionKey)
conn.Internal = true
// finish
conn.StopFirewallHandler()
@ -191,7 +191,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
// check if filtering is enabled
if !filterEnabled() {
conn.Inspecting = false
conn.SetVerdict(network.VerdictAccept, "privacy filter disabled", nil)
conn.Accept("privacy filter disabled", noReasonOptionKey)
conn.StopFirewallHandler()
issueVerdict(conn, pkt, 0, true)
return

View file

@ -36,44 +36,81 @@ import (
// 3. DecideOnConnection
// is called with the first packet of a network connection.
const noReasonOptionKey = ""
var deciders = []func(context.Context, *network.Connection, packet.Packet) bool{
checkPortmasterConnection,
checkSelfCommunication,
checkConnectionType,
checkConnectivityDomain,
checkConnectionScope,
checkEndpointLists,
checkBypassPrevention,
checkFilterLists,
dropInbound,
checkDomainHeuristics,
checkAutoPermitRelated,
}
// DecideOnConnection makes a decision about a connection.
// When called, the connection and profile is already locked.
func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) {
// update profiles and check if communication needs reevaluation
if conn.UpdateAndCheck() {
// Check if we have a process and profile.
layeredProfile := conn.Process().Profile()
if layeredProfile == nil {
conn.Deny("unknown process or profile", noReasonOptionKey)
return
}
// Check if the layered profile needs updating.
if layeredProfile.NeedsUpdate() {
// Update revision counter in connection.
conn.ProfileRevisionCounter = layeredProfile.Update()
conn.SaveWhenFinished()
// Reset verdict for connection.
log.Tracer(ctx).Infof("filter: re-evaluating verdict on %s", conn)
conn.Verdict = network.VerdictUndecided
// Reset entity if it exists.
if conn.Entity != nil {
conn.Entity.ResetLists()
}
}
var deciders = []func(context.Context, *network.Connection, packet.Packet) bool{
checkPortmasterConnection,
checkSelfCommunication,
checkProfileExists,
checkConnectionType,
checkConnectivityDomain,
checkConnectionScope,
checkEndpointLists,
checkBypassPrevention,
checkFilterLists,
checkInbound,
checkDomainHeuristics,
checkDefaultPermit,
checkAutoPermitRelated,
checkDefaultAction,
// Run all deciders and return if they came to a conclusion.
done, defaultAction := runDeciders(ctx, conn, pkt)
if done {
return
}
// Deciders did not conclude, use default action.
switch defaultAction {
case profile.DefaultActionPermit:
conn.Accept("default permit", profile.CfgOptionDefaultActionKey)
case profile.DefaultActionAsk:
prompt(ctx, conn, pkt)
default:
conn.Deny("default block", profile.CfgOptionDefaultActionKey)
}
}
func runDeciders(ctx context.Context, conn *network.Connection, pkt packet.Packet) (done bool, defaultAction uint8) {
layeredProfile := conn.Process().Profile()
// Read-lock the all the profiles.
layeredProfile.LockForUsage()
defer layeredProfile.UnlockForUsage()
// Go though all deciders, return if one sets an action.
for _, decider := range deciders {
if decider(ctx, conn, pkt) {
return
return true, profile.DefaultActionNotSet
}
}
// DefaultAction == DefaultActionBlock
conn.Deny("endpoint is not allowed (default=block)")
// Return the default action.
return false, layeredProfile.DefaultAction()
}
// checkPortmasterConnection allows all connection that originate from
@ -82,7 +119,7 @@ func checkPortmasterConnection(ctx context.Context, conn *network.Connection, pk
// grant self
if conn.Process().Pid == os.Getpid() {
log.Tracer(ctx).Infof("filter: granting own connection %s", conn)
conn.Verdict = network.VerdictAccept
conn.Accept("connection by Portmaster", noReasonOptionKey)
conn.Internal = true
return true
}
@ -115,7 +152,7 @@ func checkSelfCommunication(ctx context.Context, conn *network.Connection, pkt p
if err != nil {
log.Tracer(ctx).Warningf("filter: failed to find load local peer process with PID %d: %s", otherPid, err)
} else if otherProcess.Pid == conn.Process().Pid {
conn.Accept("connection to self")
conn.Accept("connection to self", noReasonOptionKey)
conn.Internal = true
return true
}
@ -126,14 +163,6 @@ func checkSelfCommunication(ctx context.Context, conn *network.Connection, pkt p
return false
}
func checkProfileExists(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
if conn.Process().Profile() == nil {
conn.Block("unknown process or profile")
return true
}
return false
}
func checkEndpointLists(ctx context.Context, conn *network.Connection, _ packet.Packet) bool {
var result endpoints.EPResult
var reason endpoints.Reason
@ -142,17 +171,20 @@ func checkEndpointLists(ctx context.Context, conn *network.Connection, _ packet.
p := conn.Process().Profile()
// check endpoints list
var optionKey string
if conn.Inbound {
result, reason = p.MatchServiceEndpoint(ctx, conn.Entity)
optionKey = profile.CfgOptionServiceEndpointsKey
} else {
result, reason = p.MatchEndpoint(ctx, conn.Entity)
optionKey = profile.CfgOptionEndpointsKey
}
switch result {
case endpoints.Denied:
conn.DenyWithContext(reason.String(), reason.Context())
conn.DenyWithContext(reason.String(), optionKey, reason.Context())
return true
case endpoints.Permitted:
conn.AcceptWithContext(reason.String(), reason.Context())
conn.AcceptWithContext(reason.String(), optionKey, reason.Context())
return true
}
@ -167,16 +199,16 @@ func checkConnectionType(ctx context.Context, conn *network.Connection, _ packet
case network.IncomingLAN, network.IncomingInternet, network.IncomingInvalid:
if p.BlockInbound() {
if conn.Scope == network.IncomingHost {
conn.Block("inbound connections blocked")
conn.Block("inbound connections blocked", profile.CfgOptionBlockInboundKey)
} else {
conn.Drop("inbound connections blocked")
conn.Drop("inbound connections blocked", profile.CfgOptionBlockInboundKey)
}
return true
}
case network.PeerInternet:
// BlockP2P only applies to connections to the Internet
if p.BlockP2P() {
conn.Block("direct connections (P2P) blocked")
conn.Block("direct connections (P2P) blocked", profile.CfgOptionBlockP2PKey)
return true
}
}
@ -202,7 +234,7 @@ func checkConnectivityDomain(_ context.Context, conn *network.Connection, _ pack
case netenv.IsConnectivityDomain(conn.Entity.Domain):
// Special grant!
conn.Accept("special grant for connectivity domain during network bootstrap")
conn.Accept("special grant for connectivity domain during network bootstrap", noReasonOptionKey)
return true
default:
@ -221,29 +253,29 @@ func checkConnectionScope(_ context.Context, conn *network.Connection, _ packet.
switch classification {
case netutils.Global, netutils.GlobalMulticast:
if p.BlockScopeInternet() {
conn.Deny("Internet access blocked") // Block Outbound / Drop Inbound
conn.Deny("Internet access blocked", profile.CfgOptionBlockScopeInternetKey) // Block Outbound / Drop Inbound
return true
}
case netutils.SiteLocal, netutils.LinkLocal, netutils.LocalMulticast:
if p.BlockScopeLAN() {
conn.Block("LAN access blocked") // Block Outbound / Drop Inbound
conn.Block("LAN access blocked", profile.CfgOptionBlockScopeLANKey) // Block Outbound / Drop Inbound
return true
}
case netutils.HostLocal:
if p.BlockScopeLocal() {
conn.Block("Localhost access blocked") // Block Outbound / Drop Inbound
conn.Block("Localhost access blocked", profile.CfgOptionBlockScopeLocalKey) // Block Outbound / Drop Inbound
return true
}
default: // netutils.Invalid
conn.Deny("invalid IP") // Block Outbound / Drop Inbound
conn.Deny("invalid IP", noReasonOptionKey) // Block Outbound / Drop Inbound
return true
}
} else if conn.Entity.Domain != "" {
// DNS Query
// DNS is expected to resolve to LAN or Internet addresses
// TODO: handle domains mapped to localhost
// This is a DNS Request.
// DNS is expected to resolve to LAN or Internet addresses.
// Localhost queries are immediately responded to by the nameserver.
if p.BlockScopeInternet() && p.BlockScopeLAN() {
conn.Block("Internet and LAN access blocked")
conn.Block("Internet and LAN access blocked", profile.CfgOptionBlockScopeInternetKey)
return true
}
}
@ -256,10 +288,10 @@ func checkBypassPrevention(_ context.Context, conn *network.Connection, _ packet
result, reason, reasonCtx := PreventBypassing(conn)
switch result {
case endpoints.Denied:
conn.BlockWithContext("bypass prevention: "+reason, reasonCtx)
conn.BlockWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx)
return true
case endpoints.Permitted:
conn.AcceptWithContext("bypass prevention: "+reason, reasonCtx)
conn.AcceptWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx)
return true
case endpoints.NoMatch:
}
@ -274,7 +306,7 @@ func checkFilterLists(ctx context.Context, conn *network.Connection, pkt packet.
result, reason := p.MatchFilterLists(ctx, conn.Entity)
switch result {
case endpoints.Denied:
conn.DenyWithContext(reason.String(), reason.Context())
conn.DenyWithContext(reason.String(), profile.CfgOptionFilterListsKey, reason.Context())
return true
case endpoints.NoMatch:
// nothing to do
@ -315,7 +347,7 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack
domainToCheck,
score,
)
conn.Block("possible DGA domain commonly used by malware")
conn.Block("possible DGA domain commonly used by malware", profile.CfgOptionDomainHeuristicsKey)
return true
}
log.Tracer(ctx).Tracef("filter: LMS score of eTLD+1 %s is %.2f", etld1, score)
@ -335,7 +367,7 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack
domainToCheck,
score,
)
conn.Block("possible data tunnel for covert communication and protection bypassing")
conn.Block("possible data tunnel for covert communication and protection bypassing", profile.CfgOptionDomainHeuristicsKey)
return true
}
log.Tracer(ctx).Tracef("filter: LMS score of entire domain is %.2f", score)
@ -344,20 +376,10 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack
return false
}
func checkInbound(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
func dropInbound(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
// implicit default=block for inbound
if conn.Inbound {
conn.Drop("endpoint is not allowed (incoming is always default=block)")
return true
}
return false
}
func checkDefaultPermit(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
// check default action
p := conn.Process().Profile()
if p.DefaultAction() == profile.DefaultActionPermit {
conn.Accept("endpoint is not blocked (default=permit)")
conn.Drop("incoming connection blocked by default", profile.CfgOptionServiceEndpointsKey)
return true
}
return false
@ -365,22 +387,24 @@ func checkDefaultPermit(_ context.Context, conn *network.Connection, _ packet.Pa
func checkAutoPermitRelated(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
p := conn.Process().Profile()
if !p.DisableAutoPermit() {
related, reason := checkRelation(conn)
if related {
conn.Accept(reason)
return true
}
}
return false
}
func checkDefaultAction(_ context.Context, conn *network.Connection, pkt packet.Packet) bool {
p := conn.Process().Profile()
if p.DefaultAction() == profile.DefaultActionAsk {
prompt(conn, pkt)
// Auto permit is disabled for default action permit.
if p.DefaultAction() == profile.DefaultActionPermit {
return false
}
// Check if auto permit is disabled.
if p.DisableAutoPermit() {
return false
}
// Check for relation to auto permit.
related, reason := checkRelation(conn)
if related {
conn.Accept(reason, profile.CfgOptionDisableAutoPermitKey)
return true
}
return false
}
@ -426,7 +450,7 @@ matchLoop:
}
if related {
reason = fmt.Sprintf("domain is related to process: %s is related to %s", domainElement, processElement)
reason = fmt.Sprintf("auto permitted: domain is related to process: %s is related to %s", domainElement, processElement)
}
return related, reason
}

View file

@ -197,11 +197,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
// A reason for this might be that the request is sink-holed to a forced
// IP address in which case we "accept" it, but let the firewall handle
// the resolving as it wishes.
if responder, ok := conn.ReasonContext.(nsutil.Responder); ok {
if responder, ok := conn.Reason.Context.(nsutil.Responder); ok {
// Save the request as open, as we don't know if there will be a connection or not.
network.SaveOpenDNSRequest(conn)
tracer.Infof("nameserver: handing over request for %s to special filter responder: %s", q.ID(), conn.Reason)
tracer.Infof("nameserver: handing over request for %s to special filter responder: %s", q.ID(), conn.Reason.Msg)
return reply(responder)
}
@ -243,11 +243,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
rrCache = firewall.DecideOnResolvedDNS(ctx, conn, q, rrCache)
if rrCache == nil {
// Check again if there is a responder from the firewall.
if responder, ok := conn.ReasonContext.(nsutil.Responder); ok {
if responder, ok := conn.Reason.Context.(nsutil.Responder); ok {
// Save the request as open, as we don't know if there will be a connection or not.
network.SaveOpenDNSRequest(conn)
tracer.Infof("nameserver: handing over request for %s to filter responder: %s", q.ID(), conn.Reason)
tracer.Infof("nameserver: handing over request for %s to filter responder: %s", q.ID(), conn.Reason.Msg)
return reply(responder)
}

View file

@ -83,12 +83,10 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
// The verdict may change so any access to it must be guarded by the
// connection lock.
Verdict Verdict
// Reason is a human readable description justifying the set verdict.
// Reason holds information justifying the verdict, as well as additional
// information about the reason.
// Access to Reason must be guarded by the connection lock.
Reason string
// ReasonContext may holds additional reason-specific information and
// any access must be guarded by the connection lock.
ReasonContext interface{}
Reason Reason
// Started holds the number of seconds in UNIX epoch time at which
// the connection has been initated and first seen by the portmaster.
// Staretd is only every set when creating a new connection object
@ -141,10 +139,26 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
// inspectorData holds additional meta data for the inspectors.
// using the inspectors index as a map key.
inspectorData map[uint8]interface{}
// profileRevisionCounter is used to track changes to the process
// ProfileRevisionCounter is used to track changes to the process
// profile and required for correct re-evaluation of a connections
// verdict.
profileRevisionCounter uint64
ProfileRevisionCounter uint64
}
// Reason holds information justifying a verdict, as well as additional
// information about the reason.
type Reason struct {
// Msg is a human readable description of the reason.
Msg string
// OptionKey is the configuration option key of the setting that
// was responsible for the verdict.
OptionKey string
// Profile is the database key of the profile that held the setting
// that was responsible for the verdict.
Profile string
// ReasonContext may hold additional reason-specific information and
// any access must be guarded by the connection lock.
Context interface{}
}
func getProcessContext(proc *process.Process) ProcessContext {
@ -290,7 +304,7 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
Entity: entity,
// meta
Started: time.Now().Unix(),
profileRevisionCounter: proc.Profile().RevisionCnt(),
ProfileRevisionCounter: proc.Profile().RevisionCnt(),
}
}
@ -300,73 +314,77 @@ func GetConnection(id string) (*Connection, bool) {
}
// AcceptWithContext accepts the connection.
func (conn *Connection) AcceptWithContext(reason string, ctx interface{}) {
if !conn.SetVerdict(VerdictAccept, reason, ctx) {
func (conn *Connection) AcceptWithContext(reason, reasonOptionKey string, ctx interface{}) {
if !conn.SetVerdict(VerdictAccept, reason, reasonOptionKey, ctx) {
log.Warningf("filter: tried to accept %s, but current verdict is %s", conn, conn.Verdict)
}
}
// Accept is like AcceptWithContext but only accepts a reason.
func (conn *Connection) Accept(reason string) {
conn.AcceptWithContext(reason, nil)
func (conn *Connection) Accept(reason, reasonOptionKey string) {
conn.AcceptWithContext(reason, reasonOptionKey, nil)
}
// BlockWithContext blocks the connection.
func (conn *Connection) BlockWithContext(reason string, ctx interface{}) {
if !conn.SetVerdict(VerdictBlock, reason, ctx) {
func (conn *Connection) BlockWithContext(reason, reasonOptionKey string, ctx interface{}) {
if !conn.SetVerdict(VerdictBlock, reason, reasonOptionKey, ctx) {
log.Warningf("filter: tried to block %s, but current verdict is %s", conn, conn.Verdict)
}
}
// Block is like BlockWithContext but does only accepts a reason.
func (conn *Connection) Block(reason string) {
conn.BlockWithContext(reason, nil)
func (conn *Connection) Block(reason, reasonOptionKey string) {
conn.BlockWithContext(reason, reasonOptionKey, nil)
}
// DropWithContext drops the connection.
func (conn *Connection) DropWithContext(reason string, ctx interface{}) {
if !conn.SetVerdict(VerdictDrop, reason, ctx) {
func (conn *Connection) DropWithContext(reason, reasonOptionKey string, ctx interface{}) {
if !conn.SetVerdict(VerdictDrop, reason, reasonOptionKey, ctx) {
log.Warningf("filter: tried to drop %s, but current verdict is %s", conn, conn.Verdict)
}
}
// Drop is like DropWithContext but does only accepts a reason.
func (conn *Connection) Drop(reason string) {
conn.DropWithContext(reason, nil)
func (conn *Connection) Drop(reason, reasonOptionKey string) {
conn.DropWithContext(reason, reasonOptionKey, nil)
}
// DenyWithContext blocks or drops the link depending on the connection direction.
func (conn *Connection) DenyWithContext(reason string, ctx interface{}) {
func (conn *Connection) DenyWithContext(reason, reasonOptionKey string, ctx interface{}) {
if conn.Inbound {
conn.DropWithContext(reason, ctx)
conn.DropWithContext(reason, reasonOptionKey, ctx)
} else {
conn.BlockWithContext(reason, ctx)
conn.BlockWithContext(reason, reasonOptionKey, ctx)
}
}
// Deny is like DenyWithContext but only accepts a reason.
func (conn *Connection) Deny(reason string) {
conn.DenyWithContext(reason, nil)
func (conn *Connection) Deny(reason, reasonOptionKey string) {
conn.DenyWithContext(reason, reasonOptionKey, nil)
}
// FailedWithContext marks the connection with VerdictFailed and stores the reason.
func (conn *Connection) FailedWithContext(reason string, ctx interface{}) {
if !conn.SetVerdict(VerdictFailed, reason, ctx) {
func (conn *Connection) FailedWithContext(reason, reasonOptionKey string, ctx interface{}) {
if !conn.SetVerdict(VerdictFailed, reason, reasonOptionKey, ctx) {
log.Warningf("filter: tried to drop %s due to error but current verdict is %s", conn, conn.Verdict)
}
}
// Failed is like FailedWithContext but only accepts a string.
func (conn *Connection) Failed(reason string) {
conn.FailedWithContext(reason, nil)
func (conn *Connection) Failed(reason, reasonOptionKey string) {
conn.FailedWithContext(reason, reasonOptionKey, nil)
}
// SetVerdict sets a new verdict for the connection, making sure it does not interfere with previous verdicts.
func (conn *Connection) SetVerdict(newVerdict Verdict, reason string, reasonCtx interface{}) (ok bool) {
func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey string, reasonCtx interface{}) (ok bool) {
if newVerdict >= conn.Verdict {
conn.Verdict = newVerdict
conn.Reason = reason
conn.ReasonContext = reasonCtx
conn.Reason.Msg = reason
conn.Reason.Context = reasonCtx
if reasonOptionKey != "" && conn.Process() != nil {
conn.Reason.OptionKey = reasonOptionKey
conn.Reason.Profile = conn.Process().Profile().GetProfileSource(conn.Reason.OptionKey)
}
return true
}
return false
@ -490,7 +508,7 @@ func (conn *Connection) packetHandler() {
defaultFirewallHandler(conn, pkt)
}
// log verdict
log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.Verdict.Verb(), conn.Reason)
log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.Verdict.Verb(), conn.Reason.Msg)
// save does not touch any changing data
// must not be locked, will deadlock with cleaner functions

View file

@ -124,15 +124,15 @@ func (conn *Connection) GetExtraRRs(ctx context.Context, request *dns.Msg) []dns
}
// Create resource record with verdict and reason.
rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.Verdict.Verb(), conn.Reason))
rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.Verdict.Verb(), conn.Reason.Msg))
if err != nil {
log.Tracer(ctx).Warningf("filter: failed to add informational record to reply: %s", err)
return nil
}
extra := []dns.RR{rr}
// Add additional records from ReasonContext.
if rrProvider, ok := conn.ReasonContext.(nsutil.RRProvider); ok {
// Add additional records from Reason.Context.
if rrProvider, ok := conn.Reason.Context.(nsutil.RRProvider); ok {
rrs := rrProvider.GetExtraRRs(ctx, request)
extra = append(extra, rrs...)
}

View file

@ -26,7 +26,7 @@ type LayeredProfile struct {
localProfile *Profile
layers []*Profile
revisionCounter uint64
RevisionCounter uint64
validityFlag *abool.AtomicBool
validityFlagLock sync.Mutex