This commit is contained in:
Daniel Hååvi 2025-04-03 14:41:34 +02:00 committed by GitHub
commit 18b381db4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 1608 additions and 0 deletions

View file

@ -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
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)
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
t.wg.Add(1)
interval := time.NewTicker(time.Second)
defer func() {
t.ready = false
t.wg.Done()
interval.Stop()
}()
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 {
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
}

View file

@ -0,0 +1,26 @@
package main
import (
"fmt"
"os"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/systemdns/dnsevtlog"
)
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()
}

View file

@ -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.

View file

@ -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.

View file

@ -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"`
}

View file

@ -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
)

View file

@ -0,0 +1,178 @@
// 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 (
"bytes"
"encoding/xml"
"fmt"
"io"
"strings"
"unicode/utf16"
"unicode/utf8"
"unsafe"
"golang.org/x/sys/windows"
)
// DecodeUTF16 to UTF8 bytes
func DecodeUTF16(b []byte) ([]byte, error) {
if len(b)%2 != 0 {
return nil, fmt.Errorf("must have even length byte slice")
}
u16s := make([]uint16, 1)
ret := &bytes.Buffer{}
b8buf := make([]byte, 4)
lb := len(b)
for i := 0; i < lb; i += 2 {
u16s[0] = uint16(b[i]) + (uint16(b[i+1]) << 8)
r := utf16.Decode(u16s)
n := utf8.EncodeRune(b8buf, r[0])
ret.Write(b8buf[:n])
}
return ret.Bytes(), nil
}
// GetFromSnapProcess finds information about process by the given pid
// Returns process parent pid, threads info handle and process name
func GetFromSnapProcess(pid uint32) (uint32, uint32, string, error) {
snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(pid))
if err != nil {
return 0, 0, "", err
}
defer windows.CloseHandle(snap)
var pe32 windows.ProcessEntry32
pe32.Size = uint32(unsafe.Sizeof(pe32))
if err = windows.Process32First(snap, &pe32); err != nil {
return 0, 0, "", err
}
for {
if pe32.ProcessID == uint32(pid) {
szexe := windows.UTF16ToString(pe32.ExeFile[:])
return uint32(pe32.ParentProcessID), uint32(pe32.Threads), szexe, nil
}
if err = windows.Process32Next(snap, &pe32); err != nil {
break
}
}
return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid)
}
type xmlnode struct {
XMLName xml.Name
Attrs []xml.Attr `xml:"-"`
Content []byte `xml:",innerxml"`
Text string `xml:",chardata"`
Nodes []xmlnode `xml:",any"`
}
// EventField for unique rendering
type EventField struct {
Name string
Value string
}
// UnmarshalXML redefined for xml elements walk
func (n *xmlnode) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
n.Attrs = start.Attr
type node xmlnode
return d.DecodeElement((*node)(n), &start)
}
// UnrollXMLFields extracts fields from xml data
func UnrollXMLFields(data []byte, fieldsUsage map[string]int, separator string) ([]EventField, map[string]int) {
buf := bytes.NewBuffer(data)
dec := xml.NewDecoder(buf)
var fields []EventField
for {
var node xmlnode
err := dec.Decode(&node)
if err == io.EOF {
break
}
if err != nil {
// log.Fatal(err)
break
}
var parents []string
walkXML([]xmlnode{node}, parents, separator, func(node xmlnode, parents []string, separator string) bool {
innerText := strings.TrimSpace(node.Text)
if len(innerText) > 0 {
valueName := strings.Join(parents, separator)
fieldsUsage[valueName]++
field := EventField{Name: valueName, Value: innerText}
fields = append(fields, field)
}
return true
})
}
return fields, fieldsUsage
}
func walkXML(nodes []xmlnode, parents []string, separator string, f func(xmlnode, []string, string) bool) {
for _, node := range nodes {
parentName := node.XMLName.Local
for _, attr := range node.Attrs {
attrName := strings.ToLower(attr.Name.Local)
if attrName == "name" {
// Add Name attribute to parent name
parentName = strings.Join([]string{parentName, attr.Value}, separator)
}
}
nodeParents := append(parents, parentName)
if f(node, nodeParents, separator) {
walkXML(node.Nodes, nodeParents, separator, f)
}
}
}
// UniqueFieldNames forms unique field names
// by adding _<num> if there are several of them
func UniqueFieldNames(fields []EventField, fieldsUsage map[string]int, separator string) []EventField {
var fieldsCounter = map[string]int{}
var fieldsUnique []EventField
for _, field := range fields {
fieldName := field.Name
if fieldsUsage[field.Name] > 1 {
fieldsCounter[field.Name]++
fieldName = fmt.Sprint(field.Name, separator, fieldsCounter[field.Name])
}
fieldsUnique = append(fieldsUnique, EventField{
Name: fieldName,
Value: field.Value,
})
}
return fieldsUnique
}

View file

@ -0,0 +1,223 @@
// 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 (
"bytes"
"encoding/binary"
"encoding/xml"
"io"
"reflect"
"testing"
"unicode/utf16"
)
func TestDecodeUTF16(t *testing.T) {
testString := "Test String"
utf16s := utf16.Encode([]rune(testString))
var bytesUtf16 bytes.Buffer
writer := io.Writer(&bytesUtf16)
lb := len(utf16s)
for i := 0; i < lb; i++ {
word := make([]byte, 2)
binary.LittleEndian.PutUint16(word, utf16s[i])
_, err := writer.Write(word)
if err != nil {
t.Errorf("error preparing UTF-16 test string")
return
}
}
type args struct {
b []byte
}
tests := []struct {
name string
args args
want []byte
wantErr bool
}{
{
name: "Wrong UTF-16",
args: args{b: append(bytesUtf16.Bytes(), byte('\x00'))},
wantErr: true,
},
{
name: "UTF-16",
args: args{b: bytesUtf16.Bytes()},
want: []byte(testString),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := DecodeUTF16(tt.args.b)
if (err != nil) != tt.wantErr {
t.Errorf("DecodeUTF16() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("DecodeUTF16() = %v, want %v", got, tt.want)
}
})
}
}
var xmlbroken = `
<BrokenXML>
<Data/>qq</Data>
</BrokenXML>
`
var xmldata = `
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<UserData>
<CbsPackageChangeState xmlns="http://manifests.microsoft.com/win/2004/08/windows/setup_provider">
<IntendedPackageState>5111</IntendedPackageState>
<ErrorCode><Code>0x0</Code></ErrorCode>
</CbsPackageChangeState>
</UserData>
<EventData>
<Data>2120-07-26T15:24:25Z</Data>
<Data>RulesEngine</Data>
<Data Name="Engine">RulesEngine</Data>
</EventData>
</Event>
`
type testEvent struct {
UserData struct {
InnerXML []byte `xml:",innerxml"`
} `xml:"UserData"`
EventData struct {
InnerXML []byte `xml:",innerxml"`
} `xml:"EventData"`
}
func TestUnrollXMLFields(t *testing.T) {
container := testEvent{}
err := xml.Unmarshal([]byte(xmldata), &container)
if err != nil {
t.Errorf("couldn't unmarshal precooked xml string xmldata")
return
}
type args struct {
data []byte
fieldsUsage map[string]int
}
tests := []struct {
name string
args args
want1 []EventField
want2 map[string]int
}{
{
name: "Broken XML",
args: args{
data: []byte(xmlbroken),
fieldsUsage: map[string]int{},
},
want1: nil,
want2: map[string]int{},
},
{
name: "EventData with non-unique names and one Name attr",
args: args{
data: container.EventData.InnerXML,
fieldsUsage: map[string]int{},
},
want1: []EventField{
{Name: "Data", Value: "2120-07-26T15:24:25Z"},
{Name: "Data", Value: "RulesEngine"},
{Name: "Data_Engine", Value: "RulesEngine"},
},
want2: map[string]int{"Data": 2, "Data_Engine": 1},
},
{
name: "UserData with non-unique names and three levels of depth",
args: args{
data: container.UserData.InnerXML,
fieldsUsage: map[string]int{},
},
want1: []EventField{
{Name: "CbsPackageChangeState_IntendedPackageState", Value: "5111"},
{Name: "CbsPackageChangeState_ErrorCode_Code", Value: "0x0"},
},
want2: map[string]int{
"CbsPackageChangeState_ErrorCode_Code": 1,
"CbsPackageChangeState_IntendedPackageState": 1,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1 := UnrollXMLFields(tt.args.data, tt.args.fieldsUsage, "_")
if !reflect.DeepEqual(got, tt.want1) {
t.Errorf("ExtractFields() got = %v, want %v", got, tt.want1)
}
if !reflect.DeepEqual(got1, tt.want2) {
t.Errorf("ExtractFields() got1 = %v, want %v", got1, tt.want2)
}
})
}
}
func TestUniqueFieldNames(t *testing.T) {
type args struct {
fields []EventField
fieldsUsage map[string]int
}
tests := []struct {
name string
args args
want []EventField
}{
{
name: "Unique values",
args: args{
fields: []EventField{
{Name: "Data", Value: "2120-07-26T15:24:25Z"},
{Name: "Data", Value: "RulesEngine"},
{Name: "Engine", Value: "RulesEngine"},
},
fieldsUsage: map[string]int{"Data": 2, "Engine": 1},
},
want: []EventField{
{Name: "Data_1", Value: "2120-07-26T15:24:25Z"},
{Name: "Data_2", Value: "RulesEngine"},
{Name: "Engine", Value: "RulesEngine"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := UniqueFieldNames(tt.args.fields, tt.args.fieldsUsage, "_"); !reflect.DeepEqual(got, tt.want) {
t.Errorf("PrintFields() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -0,0 +1,340 @@
// 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 (
"bytes"
"encoding/xml"
"path/filepath"
"strings"
"syscall"
"golang.org/x/sys/windows"
)
// WinEventLog config
type WinEventLog struct {
Locale uint32 `yaml:"locale"`
EventlogName string `yaml:"eventlog_name"`
Query string `yaml:"xpath_query"`
ProcessUserData bool `yaml:"process_userdata"`
ProcessEventData bool `yaml:"process_eventdata"`
Separator string `yaml:"separator"`
OnlyFirstLineOfMessage bool `yaml:"only_first_line_of_message"`
TimeStampFromEvent bool `yaml:"timestamp_from_event"`
EventTags []string `yaml:"event_tags"`
EventFields []string `yaml:"event_fields"`
ExcludeFields []string `yaml:"exclude_fields"`
ExcludeEmpty []string `yaml:"exclude_empty"`
subscription EvtHandle
buf []byte
}
var bufferSize = 1 << 14
var description = "Input plugin to collect Windows Event Log messages"
// Description for win_eventlog
func (w *WinEventLog) Description() string {
return description
}
func (w *WinEventLog) shouldExclude(field string) (should bool) {
for _, excludePattern := range w.ExcludeFields {
// Check if field name matches excluded list
if matched, _ := filepath.Match(excludePattern, field); matched {
return true
}
}
return false
}
func (w *WinEventLog) shouldProcessField(field string) (should bool, list string) {
for _, pattern := range w.EventTags {
if matched, _ := filepath.Match(pattern, field); matched {
// Tags are not excluded
return true, "tags"
}
}
for _, pattern := range w.EventFields {
if matched, _ := filepath.Match(pattern, field); matched {
if w.shouldExclude(field) {
return false, "excluded"
}
return true, "fields"
}
}
return false, "excluded"
}
func (w *WinEventLog) shouldExcludeEmptyField(field string, fieldType string, fieldValue interface{}) (should bool) {
for _, pattern := range w.ExcludeEmpty {
if matched, _ := filepath.Match(pattern, field); matched {
switch fieldType {
case "string":
return len(fieldValue.(string)) < 1
case "int":
return fieldValue.(int) == 0
case "uint32":
return fieldValue.(uint32) == 0
}
}
}
return false
}
func EvtSubscribe(logName, xquery string) (EvtHandle, error) {
var logNamePtr, xqueryPtr *uint16
sigEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return 0, err
}
defer windows.CloseHandle(sigEvent)
logNamePtr, err = syscall.UTF16PtrFromString(logName)
if err != nil {
return 0, err
}
xqueryPtr, err = syscall.UTF16PtrFromString(xquery)
if err != nil {
return 0, err
}
subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr,
0, 0, 0, EvtSubscribeStartAtOldestRecord)
if err != nil {
return 0, err
}
return subsHandle, nil
}
func EvtSubscribeWithBookmark(logName, xquery string, bookMark EvtHandle) (EvtHandle, error) {
var logNamePtr, xqueryPtr *uint16
sigEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return 0, err
}
defer windows.CloseHandle(sigEvent)
logNamePtr, err = syscall.UTF16PtrFromString(logName)
if err != nil {
return 0, err
}
xqueryPtr, err = syscall.UTF16PtrFromString(xquery)
if err != nil {
return 0, err
}
subsHandle, err := _EvtSubscribe(0, uintptr(sigEvent), logNamePtr, xqueryPtr,
bookMark, 0, 0, EvtSubscribeStartAfterBookmark)
if err != nil {
return 0, err
}
return subsHandle, nil
}
func fetchEventHandles(subsHandle EvtHandle) ([]EvtHandle, error) {
var eventsNumber uint32
var evtReturned uint32
eventsNumber = 5
eventHandles := make([]EvtHandle, eventsNumber)
err := _EvtNext(subsHandle, eventsNumber, &eventHandles[0], 0, 0, &evtReturned)
if err != nil {
if err == ERROR_INVALID_OPERATION && evtReturned == 0 {
return nil, ERROR_NO_MORE_ITEMS
}
return nil, err
}
return eventHandles[:evtReturned], nil
}
type EventFetcher struct {
buf []byte
}
func NewEventFetcher() *EventFetcher {
return &EventFetcher{}
}
func (w *EventFetcher) FetchEvents(subsHandle EvtHandle, lang uint32) ([]Event, []EvtHandle, error) {
if w.buf == nil {
w.buf = make([]byte, bufferSize)
}
var events []Event
eventHandles, err := fetchEventHandles(subsHandle)
if err != nil {
return nil, nil, err
}
for _, eventHandle := range eventHandles {
if eventHandle != 0 {
event, err := w.renderEvent(eventHandle, lang)
if err == nil {
events = append(events, event)
}
}
}
return events, eventHandles, nil
}
func Close(handles []EvtHandle) error {
for i := 0; i < len(handles); i++ {
err := _EvtClose(handles[i])
if err != nil {
return err
}
}
return nil
}
func (w *EventFetcher) renderEvent(eventHandle EvtHandle, lang uint32) (Event, error) {
var bufferUsed, propertyCount uint32
event := Event{}
err := _EvtRender(0, eventHandle, EvtRenderEventXml, uint32(len(w.buf)), &w.buf[0], &bufferUsed, &propertyCount)
if err != nil {
return event, err
}
eventXML, err := DecodeUTF16(w.buf[:bufferUsed])
if err != nil {
return event, err
}
err = xml.Unmarshal([]byte(eventXML), &event)
if err != nil {
// We can return event without most text values,
// that way we will not loose information
// This can happen when processing Forwarded Events
return event, nil
}
publisherHandle, err := openPublisherMetadata(0, event.Source.Name, lang)
if err != nil {
return event, nil
}
defer _EvtClose(publisherHandle)
// Populating text values
keywords, err := formatEventString(EvtFormatMessageKeyword, eventHandle, publisherHandle)
if err == nil {
event.Keywords = keywords
}
message, err := formatEventString(EvtFormatMessageEvent, eventHandle, publisherHandle)
if err == nil {
event.Message = message
}
level, err := formatEventString(EvtFormatMessageLevel, eventHandle, publisherHandle)
if err == nil {
event.LevelText = level
}
task, err := formatEventString(EvtFormatMessageTask, eventHandle, publisherHandle)
if err == nil {
event.TaskText = task
}
opcode, err := formatEventString(EvtFormatMessageOpcode, eventHandle, publisherHandle)
if err == nil {
event.OpcodeText = opcode
}
return event, nil
}
func formatEventString(
messageFlag EvtFormatMessageFlag,
eventHandle EvtHandle,
publisherHandle EvtHandle,
) (string, error) {
var bufferUsed uint32
err := _EvtFormatMessage(publisherHandle, eventHandle, 0, 0, 0, messageFlag,
0, nil, &bufferUsed)
if err != nil && err != ERROR_INSUFFICIENT_BUFFER {
return "", err
}
bufferUsed *= 2
buffer := make([]byte, bufferUsed)
bufferUsed = 0
err = _EvtFormatMessage(publisherHandle, eventHandle, 0, 0, 0, messageFlag,
uint32(len(buffer)/2), &buffer[0], &bufferUsed)
bufferUsed *= 2
if err != nil {
return "", err
}
result, err := DecodeUTF16(buffer[:bufferUsed])
if err != nil {
return "", err
}
var out string
if messageFlag == EvtFormatMessageKeyword {
// Keywords are returned as array of a zero-terminated strings
splitZero := func(c rune) bool { return c == '\x00' }
eventKeywords := strings.FieldsFunc(string(result), splitZero)
// So convert them to comma-separated string
out = strings.Join(eventKeywords, ",")
} else {
result := bytes.Trim(result, "\x00")
out = string(result)
}
return out, nil
}
// openPublisherMetadata opens a handle to the publisher's metadata. Close must
// be called on returned EvtHandle when finished with the handle.
func openPublisherMetadata(
session EvtHandle,
publisherName string,
lang uint32,
) (EvtHandle, error) {
p, err := syscall.UTF16PtrFromString(publisherName)
if err != nil {
return 0, err
}
h, err := _EvtOpenPublisherMetadata(session, p, nil, lang, 0)
if err != nil {
return 0, err
}
return h, nil
}

View file

@ -0,0 +1,28 @@
// 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

View file

@ -0,0 +1,159 @@
// 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 (
"testing"
)
func TestWinEventLog_shouldExcludeEmptyField(t *testing.T) {
type args struct {
field string
fieldType string
fieldValue interface{}
}
tests := []struct {
name string
w *WinEventLog
args args
wantShould bool
}{
{
name: "Not in list",
args: args{field: "qq", fieldType: "string", fieldValue: ""},
wantShould: false,
w: &WinEventLog{ExcludeEmpty: []string{"te*"}},
},
{
name: "Empty string",
args: args{field: "test", fieldType: "string", fieldValue: ""},
wantShould: true,
w: &WinEventLog{ExcludeEmpty: []string{"te*"}},
},
{
name: "Non-empty string",
args: args{field: "test", fieldType: "string", fieldValue: "qq"},
wantShould: false,
w: &WinEventLog{ExcludeEmpty: []string{"te*"}},
},
{
name: "Zero int",
args: args{field: "test", fieldType: "int", fieldValue: int(0)},
wantShould: true,
w: &WinEventLog{ExcludeEmpty: []string{"te*"}},
},
{
name: "Non-zero int",
args: args{field: "test", fieldType: "int", fieldValue: int(-1)},
wantShould: false,
w: &WinEventLog{ExcludeEmpty: []string{"te*"}},
},
{
name: "Zero uint32",
args: args{field: "test", fieldType: "uint32", fieldValue: uint32(0)},
wantShould: true,
w: &WinEventLog{ExcludeEmpty: []string{"te*"}},
},
{
name: "Non-zero uint32",
args: args{field: "test", fieldType: "uint32", fieldValue: uint32(0xc0fefeed)},
wantShould: false,
w: &WinEventLog{ExcludeEmpty: []string{"te*"}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotShould := tt.w.shouldExcludeEmptyField(tt.args.field, tt.args.fieldType, tt.args.fieldValue); gotShould != tt.wantShould {
t.Errorf("WinEventLog.shouldExcludeEmptyField() = %v, want %v", gotShould, tt.wantShould)
}
})
}
}
func TestWinEventLog_shouldProcessField(t *testing.T) {
tags := []string{"Source", "Level*"}
fields := []string{"EventID", "Message*"}
excluded := []string{"Message*"}
type args struct {
field string
}
tests := []struct {
name string
w *WinEventLog
args args
wantShould bool
wantList string
}{
{
name: "Not in tags",
args: args{field: "test"},
wantShould: false,
wantList: "excluded",
w: &WinEventLog{EventTags: tags, EventFields: fields, ExcludeFields: excluded},
},
{
name: "In Tags",
args: args{field: "LevelText"},
wantShould: true,
wantList: "tags",
w: &WinEventLog{EventTags: tags, EventFields: fields, ExcludeFields: excluded},
},
{
name: "Not in Fields",
args: args{field: "EventId"},
wantShould: false,
wantList: "excluded",
w: &WinEventLog{EventTags: tags, EventFields: fields, ExcludeFields: excluded},
},
{
name: "In Fields",
args: args{field: "EventID"},
wantShould: true,
wantList: "fields",
w: &WinEventLog{EventTags: tags, EventFields: fields, ExcludeFields: excluded},
},
{
name: "In Fields and Excluded",
args: args{field: "Messages"},
wantShould: false,
wantList: "excluded",
w: &WinEventLog{EventTags: tags, EventFields: fields, ExcludeFields: excluded},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotShould, gotList := tt.w.shouldProcessField(tt.args.field)
if gotShould != tt.wantShould {
t.Errorf("WinEventLog.shouldProcessField() gotShould = %v, want %v", gotShould, tt.wantShould)
}
if gotList != tt.wantList {
t.Errorf("WinEventLog.shouldProcessField() gotList = %v, want %v", gotList, tt.wantList)
}
})
}
}

View file

@ -0,0 +1,229 @@
// 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"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// EvtHandle uintptr
type EvtHandle uintptr
// Do the interface allocations only once for common
// Errno values.
const (
//revive:disable-next-line:var-naming
errnoERROR_IO_PENDING = 997
)
var (
//revive:disable-next-line:var-naming
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
)
// EvtFormatMessageFlag defines the values that specify the message string from
// the event to format.
type EvtFormatMessageFlag uint32
// EVT_FORMAT_MESSAGE_FLAGS enumeration
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa385525(v=vs.85).aspx
const (
//revive:disable:var-naming
// Format the event's message string.
EvtFormatMessageEvent EvtFormatMessageFlag = iota + 1
// Format the message string of the level specified in the event.
EvtFormatMessageLevel
// Format the message string of the task specified in the event.
EvtFormatMessageTask
// Format the message string of the task specified in the event.
EvtFormatMessageOpcode
// Format the message string of the keywords specified in the event. If the
// event specifies multiple keywords, the formatted string is a list of
// null-terminated strings. Increment through the strings until your pointer
// points past the end of the used buffer.
EvtFormatMessageKeyword
//revive:enable:var-naming
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return nil
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
return e
}
var (
modwevtapi = windows.NewLazySystemDLL("wevtapi.dll")
procEvtSubscribe = modwevtapi.NewProc("EvtSubscribe")
procEvtRender = modwevtapi.NewProc("EvtRender")
procEvtClose = modwevtapi.NewProc("EvtClose")
procEvtNext = modwevtapi.NewProc("EvtNext")
procEvtFormatMessage = modwevtapi.NewProc("EvtFormatMessage")
procEvtOpenPublisherMetadata = modwevtapi.NewProc("EvtOpenPublisherMetadata")
procEvtCreateBookmark = modwevtapi.NewProc("EvtCreateBookmark")
procEvtUpdateBookmark = modwevtapi.NewProc("EvtUpdateBookmark")
)
func _EvtSubscribe(session EvtHandle, signalEvent uintptr, channelPath *uint16, query *uint16, bookmark EvtHandle, context uintptr, callback syscall.Handle, flags EvtSubscribeFlag) (handle EvtHandle, err error) {
r0, _, e1 := syscall.Syscall9(procEvtSubscribe.Addr(), 8, uintptr(session), uintptr(signalEvent), uintptr(unsafe.Pointer(channelPath)), uintptr(unsafe.Pointer(query)), uintptr(bookmark), uintptr(context), uintptr(callback), uintptr(flags), 0)
handle = EvtHandle(r0)
if handle == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _EvtRender(context EvtHandle, fragment EvtHandle, flags EvtRenderFlag, bufferSize uint32, buffer *byte, bufferUsed *uint32, propertyCount *uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procEvtRender.Addr(), 7, uintptr(context), uintptr(fragment), uintptr(flags), uintptr(bufferSize), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(bufferUsed)), uintptr(unsafe.Pointer(propertyCount)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _EvtClose(object EvtHandle) (err error) {
r1, _, e1 := syscall.Syscall(procEvtClose.Addr(), 1, uintptr(object), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _EvtNext(resultSet EvtHandle, eventArraySize uint32, eventArray *EvtHandle, timeout uint32, flags uint32, numReturned *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procEvtNext.Addr(), 6, uintptr(resultSet), uintptr(eventArraySize), uintptr(unsafe.Pointer(eventArray)), uintptr(timeout), uintptr(flags), uintptr(unsafe.Pointer(numReturned)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _EvtFormatMessage(publisherMetadata EvtHandle, event EvtHandle, messageID uint32, valueCount uint32, values uintptr, flags EvtFormatMessageFlag, bufferSize uint32, buffer *byte, bufferUsed *uint32) (err error) {
r1, _, e1 := syscall.Syscall9(procEvtFormatMessage.Addr(), 9, uintptr(publisherMetadata), uintptr(event), uintptr(messageID), uintptr(valueCount), uintptr(values), uintptr(flags), uintptr(bufferSize), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(bufferUsed)))
if r1 == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func _EvtOpenPublisherMetadata(session EvtHandle, publisherIdentity *uint16, logFilePath *uint16, locale uint32, flags uint32) (handle EvtHandle, err error) {
r0, _, e1 := syscall.Syscall6(procEvtOpenPublisherMetadata.Addr(), 5, uintptr(session), uintptr(unsafe.Pointer(publisherIdentity)), uintptr(unsafe.Pointer(logFilePath)), uintptr(locale), uintptr(flags), 0)
handle = EvtHandle(r0)
if handle == 0 {
if e1 != 0 {
err = errnoErr(e1)
} else {
err = syscall.EINVAL
}
}
return
}
// CreateBookmark create a new bookmark from a string, if the string is empty a new bookrmak is created.
// Use update the bookmark with an event to save the position, or render to get the bookmark data to store.
func CreateBookmark(bookmark string) (EvtHandle, error) {
bookmarkPtr, err := stringPointer(bookmark)
if err != nil {
return 0, err
}
r0, _, e1 := syscall.Syscall(procEvtCreateBookmark.Addr(), 1, bookmarkPtr, 0, 0)
handle := EvtHandle(r0)
if handle == 0 {
if e1 != 0 {
return 0, errnoErr(e1)
}
return 0, syscall.EINVAL
}
return handle, nil
}
func UpdateBookmark(bookmark, event EvtHandle, buf []byte) (string, error) {
r1, _, e1 := syscall.Syscall(procEvtUpdateBookmark.Addr(), 2, uintptr(bookmark), uintptr(event), 0)
if r1 == 0 {
if e1 != 0 {
return "", errnoErr(e1)
} else {
return "", syscall.EINVAL
}
}
var bufferUsed, propertyCount uint32
err := _EvtRender(0, bookmark, EvtRenderBookmark, uint32(len(buf)), &buf[0], &bufferUsed, &propertyCount)
if err != nil {
return "", err
}
bookMarkData, err := DecodeUTF16(buf[:bufferUsed])
if err != nil {
return "", err
}
return string(bookMarkData), nil
}
func stringPointer(s string) (uintptr, error) {
if s == "" {
return 0, nil
}
// last character is always nil and causes issue for decoding.
ptr, err := syscall.UTF16PtrFromString(s[:len(s)-1])
if err != nil {
return 0, err
}
return uintptr(unsafe.Pointer(ptr)), nil
}

1
systemdns/module.go Normal file
View file

@ -0,0 +1 @@
package systemdns

View file

@ -0,0 +1,96 @@
package winlogdns
import (
"fmt"
"syscall"
"unsafe"
)
var (
advapi32 = syscall.NewLazyDLL("advapi32.dll")
procOpenEventLog = advapi32.NewProc("OpenEventLogW")
procCloseEventLog = advapi32.NewProc("CloseEventLog")
procReadEventLog = advapi32.NewProc("ReadEventLogW")
)
type DNSLogReader struct {
handle uintptr
}
type Win32_NTLogEvent struct {
Logfile string
RecordID uint32
EventCode uint16
ProcessName string // Process name that generated the event
ProcessID uint32 // Process ID (PID) of the generating process
Message string // Event message containing DNS query and response details
}
func NewDNSLogListener() (*DNSLogReader, error) {
// Open event log.
lr := new(DNSLogReader)
if err := lr.openEventLog(); err != nil {
return nil, err
}
return lr, nil
}
func (lr *DNSLogReader) openEventLog() error {
// Convert strings.
host, err := syscall.UTF16PtrFromString("")
if err != nil {
return err
}
source, err := syscall.UTF16PtrFromString("DNS Client Events")
if err != nil {
return err
}
// Open event log for DNS client events.
handle, _, err := procOpenEventLog.Call(
uintptr(unsafe.Pointer(host)),
uintptr(unsafe.Pointer(source)),
)
if err != nil {
return err
}
// Set handle and return
lr.handle = handle
return nil
}
func (lr *DNSLogReader) readEventLog(readflags, recordoffset uint32, buffer []byte, numberofbytestoread uint32, bytesread, minnumberofbytesneeded *uint32) (*Win32_NTLogEvent, error) {
ret, _, err := procReadEventLog.Call(
uintptr(lr.handle),
uintptr(readflags),
uintptr(recordoffset),
uintptr(unsafe.Pointer(&buffer[0])),
uintptr(numberofbytestoread),
uintptr(unsafe.Pointer(bytesread)),
uintptr(unsafe.Pointer(minnumberofbytesneeded)))
if err != nil {
return nil, err
}
if ret != 0 {
return nil, fmt.Errorf("failed with return code %d", ret)
}
// What do I do here?
return nil, nil
}
func (lr *DNSLogReader) Close() error {
ret, _, err := procCloseEventLog.Call(lr.handle)
if err != nil {
return err
}
if ret != 0 {
return fmt.Errorf("failed with return code %d", ret)
}
return nil
}