Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Monitor System DNS client queries #1426

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions systemdns/dnsevtlog/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//go:build windows
// +build windows

package dnsevtlog

// This code is copied from Promtail v1.6.2-0.20231004111112-07cbef92268a with minor changes.

import (
"fmt"
"sync"
"time"

"golang.org/x/sys/windows"

"github.com/safing/portbase/log"
"github.com/safing/portmaster/systemdns/dnsevtlog/win_eventlog"
)

type Subscription struct {
subscription win_eventlog.EvtHandle
fetcher *win_eventlog.EventFetcher

eventLogName string
eventLogQuery string

ready bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ready is never read anywhere so I guess we can drop it.

done chan struct{}
wg sync.WaitGroup
err error
}

// NewSubscription create a new windows event subscriptions.
func NewSubscription() (*Subscription, error) {
sigEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return nil, err
}
defer windows.CloseHandle(sigEvent)
Comment on lines +34 to +38
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? sigEvent is not used anymore in the function body.


t := &Subscription{
eventLogName: "Microsoft-Windows-DNS-Client/Operational",
eventLogQuery: "*",
done: make(chan struct{}),
fetcher: win_eventlog.NewEventFetcher(),
}

subsHandle, err := win_eventlog.EvtSubscribe(t.eventLogName, t.eventLogQuery)
if err != nil {
return nil, fmt.Errorf("error subscribing to windows events: %w", err)
}
t.subscription = subsHandle

return t, nil
}

// loop fetches new events and send them to via the Loki client.
func (t *Subscription) ReadWorker() {
t.ready = true
Comment on lines +55 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment needs to be updated

t.wg.Add(1)
interval := time.NewTicker(time.Second)
defer func() {
t.ready = false
t.wg.Done()
interval.Stop()
}()
Comment on lines +59 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess ReadWorker is expected to run in a goroutine. so access to read is unprotected if it's launched multiple times.
Also, wg.Add(1) should be called before the go-routine is launched since there could be a race condition when starting the ReadWorker and immediatley calling Stop which could cause a panic. See https://pkg.go.dev/sync#WaitGroup.Add.

Better add a dedicated Start() routine that adds to the wg and kicks of the goroutine


for {

loop:
for {
// fetch events until there's no more.
events, handles, err := t.fetcher.FetchEvents(t.subscription, 1033) // 1033: English
if err != nil {
if err != win_eventlog.ERROR_NO_MORE_ITEMS {
Comment on lines +73 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be errors.Is

t.err = err
log.Warningf("dns event log: failed to fetch events: %s", err)
} else {
log.Debug("dns event log: no more entries")
}
break loop
}
t.err = nil
// we have received events to handle.
for _, entry := range events {
log.Debugf("dns event log: %+v", entry)
}
win_eventlog.Close(handles)
}
// no more messages we wait for next poll timer tick.
select {
case <-t.done:
return
case <-interval.C:
}
}
}

// renderEntries renders Loki entries from windows event logs
// func (t *Subscription) renderEntries(events []win_eventlog.Event) []api.Entry {
// res := make([]api.Entry, 0, len(events))
// lbs := labels.NewBuilder(nil)
// for _, event := range events {
// entry := api.Entry{
// Labels: make(model.LabelSet),
// }

// entry.Timestamp = time.Now()
// if t.cfg.UseIncomingTimestamp {
// timeStamp, err := time.Parse(time.RFC3339Nano, fmt.Sprintf("%v", event.TimeCreated.SystemTime))
// if err != nil {
// level.Warn(t.logger).Log("msg", "error parsing timestamp", "err", err)
// } else {
// entry.Timestamp = timeStamp
// }
// }

// for _, lbl := range processed {
// if strings.HasPrefix(lbl.Name, "__") {
// continue
// }
// entry.Labels[model.LabelName(lbl.Name)] = model.LabelValue(lbl.Value)
// }

// line, err := formatLine(t.cfg, event)
// if err != nil {
// level.Warn(t.logger).Log("msg", "error formatting event", "err", err)
// continue
// }
// entry.Line = line
// res = append(res, entry)
// }
// return res
// }

func (t *Subscription) Stop() error {
close(t.done)
t.wg.Wait()
return t.err
}
26 changes: 26 additions & 0 deletions systemdns/dnsevtlog/testevtlog/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package main

import (
"fmt"
"os"

"github.com/safing/portbase/log"
"github.com/safing/portmaster/systemdns/dnsevtlog"

Check failure on line 8 in systemdns/dnsevtlog/testevtlog/main.go

View workflow job for this annotation

GitHub Actions / Linter

could not import github.com/safing/portmaster/systemdns/dnsevtlog (-: build constraints exclude all Go files in systemdns/dnsevtlog) (typecheck)
)

func main() {
log.SetLogLevel(log.DebugLevel)
err := log.Start()
if err != nil {
fmt.Print(err)
os.Exit(1)
}

sub, err := dnsevtlog.NewSubscription()
if err != nil {
fmt.Print(err)
os.Exit(1)
}

sub.ReadWorker()
}
22 changes: 22 additions & 0 deletions systemdns/dnsevtlog/win_eventlog/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

The MIT License (MIT)

Copyright (c) 2015-2020 InfluxData Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
6 changes: 6 additions & 0 deletions systemdns/dnsevtlog/win_eventlog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Windows Event Log

This is a fork of https://github.com/influxdata/telegraf/tree/master/plugins/inputs/win_eventlog to re-use most of the syscall implementation for the eventlog.

It is simplified in order to just subscribe to events.

94 changes: 94 additions & 0 deletions systemdns/dnsevtlog/win_eventlog/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// The MIT License (MIT)

// Copyright (c) 2015-2020 InfluxData Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//go:build windows
// +build windows

// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog

// Event is the event entry representation
// Only the most common elements are processed, human-readable data is rendered in Message
// More info on schema, if there will be need to add more:
// https://docs.microsoft.com/en-us/windows/win32/wes/eventschema-elements
type Event struct {
Source Provider `xml:"System>Provider"`
EventID int `xml:"System>EventID"`
Version int `xml:"System>Version"`
Level int `xml:"System>Level"`
Task int `xml:"System>Task"`
Opcode int `xml:"System>Opcode"`
Keywords string `xml:"System>Keywords"`
TimeCreated TimeCreated `xml:"System>TimeCreated"`
EventRecordID int `xml:"System>EventRecordID"`
Correlation Correlation `xml:"System>Correlation"`
Execution Execution `xml:"System>Execution"`
Channel string `xml:"System>Channel"`
Computer string `xml:"System>Computer"`
Security Security `xml:"System>Security"`
UserData UserData `xml:"UserData"`
EventData EventData `xml:"EventData"`
Message string
LevelText string
TaskText string
OpcodeText string
}

// UserData Application-provided XML data
type UserData struct {
InnerXML []byte `xml:",innerxml"`
}

// EventData Application-provided XML data
type EventData struct {
InnerXML []byte `xml:",innerxml"`
}

// Provider is the Event provider information
type Provider struct {
Name string `xml:"Name,attr"`
}

// Correlation is used for the event grouping
type Correlation struct {
ActivityID string `xml:"ActivityID,attr"`
RelatedActivityID string `xml:"RelatedActivityID,attr"`
}

// Execution Info for Event
type Execution struct {
ProcessID uint32 `xml:"ProcessID,attr"`
ThreadID uint32 `xml:"ThreadID,attr"`
ProcessName string
}

// Security Data for Event
type Security struct {
UserID string `xml:"UserID,attr"`
}

// TimeCreated field for Event
type TimeCreated struct {
SystemTime string `xml:"SystemTime,attr"`
}
67 changes: 67 additions & 0 deletions systemdns/dnsevtlog/win_eventlog/syscall_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// The MIT License (MIT)

// Copyright (c) 2015-2020 InfluxData Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//go:build windows
// +build windows

// Package win_eventlog Input plugin to collect Windows Event Log messages
//
//revive:disable-next-line:var-naming
package win_eventlog

import (
"syscall"
)

// Event log error codes.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
const (
//revive:disable:var-naming
ERROR_INSUFFICIENT_BUFFER syscall.Errno = 122
ERROR_NO_MORE_ITEMS syscall.Errno = 259
ERROR_INVALID_OPERATION syscall.Errno = 4317
//revive:enable:var-naming
)

// EvtSubscribeFlag defines the possible values that specify when to start subscribing to events.
type EvtSubscribeFlag uint32

// EVT_SUBSCRIBE_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385588(v=vs.85).aspx
const (
EvtSubscribeToFutureEvents EvtSubscribeFlag = 1
EvtSubscribeStartAtOldestRecord EvtSubscribeFlag = 2
EvtSubscribeStartAfterBookmark EvtSubscribeFlag = 3
)

// EvtRenderFlag uint32
type EvtRenderFlag uint32

// EVT_RENDER_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385563(v=vs.85).aspx
const (
//revive:disable:var-naming
// Render the event as an XML string. For details on the contents of the
// XML string, see the Event schema.
EvtRenderEventXml EvtRenderFlag = 1
//revive:enable:var-naming
EvtRenderBookmark EvtRenderFlag = 2
)
Loading
Loading