From 6858dd868426c7f859bf688a9c8c27c7171348ef Mon Sep 17 00:00:00 2001 From: Kornilios Kourtis Date: Fri, 29 Nov 2024 11:00:24 +0100 Subject: [PATCH] tetragon-oci-hook-setup: crio.conf annotations Add a command to patch crio.conf to add allowed annotations. For example: > $ ssh cat /etc/crio/crio.conf > crio.conf > $ ./tetragon-oci-hook-setup patch-crio-conf enable-annotations --config-file=crio.conf --output-file crio-patched.conf --annotations='io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw' > $ diff -u crio.conf crio-patched.conf > --- crio.conf 2024-11-29 10:47:11.622015385 +0100 > +++ crio-patched.conf 2024-11-29 11:03:39.856306109 +0100 > @@ -300,6 +300,7 @@ > runtime_path = "/usr/bin/runc" > runtime_type = "oci" > runtime_root = "/run/runc" > +allowed_annotations = ["io.kubernetes.cri-o.cgroup2-mount-hierarchy-rw"] Signed-off-by: Kornilios Kourtis --- contrib/tetragon-rthooks/cmd/setup/addline.go | 1 + contrib/tetragon-rthooks/cmd/setup/main.go | 1 + .../cmd/setup/patch-crio-conf.go | 136 ++ contrib/tetragon-rthooks/go.mod | 5 +- contrib/tetragon-rthooks/go.sum | 11 +- .../github.com/godbus/dbus/v5/.cirrus.yml | 10 + .../github.com/godbus/dbus/v5/.golangci.yml | 7 + .../github.com/godbus/dbus/v5/README.md | 3 +- .../github.com/godbus/dbus/v5/SECURITY.md | 13 + .../vendor/github.com/godbus/dbus/v5/auth.go | 44 +- .../vendor/github.com/godbus/dbus/v5/call.go | 3 - .../vendor/github.com/godbus/dbus/v5/conn.go | 43 +- .../github.com/godbus/dbus/v5/conn_darwin.go | 1 - .../github.com/godbus/dbus/v5/conn_other.go | 10 +- .../github.com/godbus/dbus/v5/conn_unix.go | 3 +- .../github.com/godbus/dbus/v5/conn_windows.go | 2 - .../vendor/github.com/godbus/dbus/v5/dbus.go | 17 +- .../github.com/godbus/dbus/v5/decoder.go | 193 ++- .../godbus/dbus/v5/default_handler.go | 18 +- .../vendor/github.com/godbus/dbus/v5/doc.go | 43 +- .../github.com/godbus/dbus/v5/export.go | 19 +- .../vendor/github.com/godbus/dbus/v5/match.go | 8 +- .../github.com/godbus/dbus/v5/message.go | 17 +- .../github.com/godbus/dbus/v5/object.go | 13 +- .../godbus/dbus/v5/sequential_handler.go | 2 +- .../godbus/dbus/v5/server_interfaces.go | 2 +- .../vendor/github.com/godbus/dbus/v5/sig.go | 15 +- .../godbus/dbus/v5/transport_nonce_tcp.go | 3 +- .../godbus/dbus/v5/transport_unix.go | 136 +- .../dbus/v5/transport_unixcred_freebsd.go | 38 +- .../godbus/dbus/v5/variant_parser.go | 2 - .../github.com/pelletier/go-toml/v2/LICENSE | 22 + .../go-toml/v2/internal/characters/ascii.go | 42 + .../go-toml/v2/internal/characters/utf8.go | 199 +++ .../go-toml/v2/internal/danger/danger.go | 65 + .../go-toml/v2/internal/danger/typeid.go | 23 + .../pelletier/go-toml/v2/unstable/ast.go | 136 ++ .../pelletier/go-toml/v2/unstable/builder.go | 71 + .../pelletier/go-toml/v2/unstable/doc.go | 3 + .../pelletier/go-toml/v2/unstable/kind.go | 71 + .../pelletier/go-toml/v2/unstable/parser.go | 1245 +++++++++++++++++ .../pelletier/go-toml/v2/unstable/scanner.go | 270 ++++ .../go-toml/v2/unstable/unmarshaler.go | 7 + contrib/tetragon-rthooks/vendor/modules.txt | 9 +- 44 files changed, 2735 insertions(+), 247 deletions(-) create mode 100644 contrib/tetragon-rthooks/cmd/setup/patch-crio-conf.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/.cirrus.yml create mode 100644 contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/.golangci.yml create mode 100644 contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/SECURITY.md create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/LICENSE create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go create mode 100644 contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go diff --git a/contrib/tetragon-rthooks/cmd/setup/addline.go b/contrib/tetragon-rthooks/cmd/setup/addline.go index 333701362fe..2637d28e1f0 100644 --- a/contrib/tetragon-rthooks/cmd/setup/addline.go +++ b/contrib/tetragon-rthooks/cmd/setup/addline.go @@ -48,6 +48,7 @@ func applyChanges(fnameIn, fnameOut string, changes []addLine) error { for i := range changes { ch := &changes[i] if ch.pos.Line == inLine { + // NB: we assume that everything before is indentation line := strings.Repeat(" ", ch.pos.Col-1) + ch.line + cr lines = append(lines, line) if ch.replaceLine { diff --git a/contrib/tetragon-rthooks/cmd/setup/main.go b/contrib/tetragon-rthooks/cmd/setup/main.go index eb1c2991e9e..5845046a39e 100644 --- a/contrib/tetragon-rthooks/cmd/setup/main.go +++ b/contrib/tetragon-rthooks/cmd/setup/main.go @@ -232,6 +232,7 @@ type CLI struct { Uninstall Uninstall `cmd:"" help:"Uninstall hook"` PrintConfig PrintConfig `cmd:"" help:"Print config"` PatchContainerdConf patchContainerdConf `cmd:"patch containerd configuration"` + PatchCrioConf patchCrioConf `cmd:"patch crio configuration"` LogLevel string `name:"log-level" default:"info" help:"log level"` } diff --git a/contrib/tetragon-rthooks/cmd/setup/patch-crio-conf.go b/contrib/tetragon-rthooks/cmd/setup/patch-crio-conf.go new file mode 100644 index 00000000000..052003188fd --- /dev/null +++ b/contrib/tetragon-rthooks/cmd/setup/patch-crio-conf.go @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package main + +import ( + "fmt" + "log/slog" + "os" + "strings" + + "github.com/pelletier/go-toml" + tomlparser "github.com/pelletier/go-toml/v2/unstable" +) + +type patchCrioConf struct { + EnableAnnotations enableAnnotations `cmd:"" help:"enable annotations"` +} + +type enableAnnotations struct { + ConfFile string `name:"config-file" default:"/etc/crio/crio.conf" help:"crio configuration file location (input) (${default}))"` + Outfile string `name:"output-file" default:"" help:"output file location"` + Annotations []string `name:"annotations"` +} + +func doEnableAnnotations(log *slog.Logger, c *enableAnnotations) ([]addLine, error) { + data, err := os.ReadFile(c.ConfFile) + if err != nil { + return nil, err + } + + p := tomlparser.Parser{ + KeepComments: true, + } + p.Reset(data) + + lines := []addLine{} + insideRuntime := false + var annotationsLoc tomlparser.Shape + for p.NextExpression() { + e := p.Expression() + switch insideRuntime { + case false: + if e.Kind == tomlparser.Table { + c := e.Child() + if c == nil || c.Kind != tomlparser.Key || string(c.Data) != "crio" { + continue + } + c = c.Next() + if c == nil || c.Kind != tomlparser.Key || string(c.Data) != "runtime" { + continue + } + c = c.Next() + if c == nil || c.Kind != tomlparser.Key || string(c.Data) != "runtimes" { + continue + } + c = c.Next() + if c == nil || c.Kind != tomlparser.Key { + continue + } + insideRuntime = true + } + case true: + foundAnnotations := false + done := false + annotations := []string{} + if e.Kind == tomlparser.KeyValue { + var array *tomlparser.Node + for c := e.Child(); c != nil; c = c.Next() { + annotationsLoc = p.Shape(c.Raw) + if c.Kind == tomlparser.Key && string(c.Data) == "allowed_annotations" { + foundAnnotations = true + } else if c.Kind == tomlparser.Array { + array = c + } + + if foundAnnotations && array != nil { + for cc := array.Child(); cc != nil; cc = cc.Next() { + if cc.Kind == tomlparser.String { + annotations = append(annotations, string(cc.Data)) + } + } + break + } + } + + } else { + done = true + } + + if foundAnnotations || done { + annotations = append(annotations, c.Annotations...) + qannotations := make([]string, 0, len(annotations)) + for _, a := range annotations { + qannotations = append(qannotations, fmt.Sprintf("%q", a)) + } + insideRuntime = false + lines = append(lines, addLine{ + pos: toml.Position{Col: annotationsLoc.Start.Column, Line: annotationsLoc.Start.Line}, + line: fmt.Sprintf("allowed_annotations = [%s]", strings.Join(qannotations, ", ")), + replaceLine: foundAnnotations, + }) + } + } + } + + return lines, nil + +} + +func (c *enableAnnotations) Run(log *slog.Logger) error { + + changes, err := doEnableAnnotations(log, c) + if len(changes) == 0 { + log.Info("nothing to do") + return nil + } + + outFname := c.Outfile + if outFname == "" { + f, err := os.CreateTemp("", "crio.*.conf") + if err != nil { + return err + } + outFname = f.Name() + f.Close() + } + + err = applyChanges(c.ConfFile, outFname, changes) + if err != nil { + return err + } + log.Info("written output", "filename", outFname) + return nil + +} diff --git a/contrib/tetragon-rthooks/go.mod b/contrib/tetragon-rthooks/go.mod index 10a9f8c4a44..a46e6324459 100644 --- a/contrib/tetragon-rthooks/go.mod +++ b/contrib/tetragon-rthooks/go.mod @@ -16,6 +16,7 @@ require ( github.com/opencontainers/runc v1.2.2 github.com/opencontainers/runtime-spec v1.2.0 github.com/pelletier/go-toml v1.9.5 + github.com/pelletier/go-toml/v2 v2.2.3 github.com/stretchr/testify v1.10.0 google.golang.org/grpc v1.68.0 ) @@ -29,11 +30,11 @@ require ( github.com/containerd/ttrpc v1.2.6-0.20240827082320-b5cd6e4b3287 // indirect github.com/containerd/typeurl/v2 v2.2.2 // indirect github.com/containers/storage v1.56.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09 // indirect github.com/cyphar/filepath-securejoin v0.3.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/kr/text v0.2.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect diff --git a/contrib/tetragon-rthooks/go.sum b/contrib/tetragon-rthooks/go.sum index 17fcd8bed11..8875c9411bd 100644 --- a/contrib/tetragon-rthooks/go.sum +++ b/contrib/tetragon-rthooks/go.sum @@ -33,8 +33,8 @@ github.com/containers/common v0.61.0 h1:j/84PTqZIKKYy42OEJsZmjZ4g4Kq2ERuC3tqp2yW github.com/containers/common v0.61.0/go.mod h1:NGRISq2vTFPSbhNqj6MLwyes4tWSlCnqbJg7R77B8xc= github.com/containers/storage v1.56.0 h1:DZ9KSkj6M2tvj/4bBoaJu3QDHRl35BwsZ4kmLJS97ZI= github.com/containers/storage v1.56.0/go.mod h1:c6WKowcAlED/DkWGNuL9bvGYqIWCVy7isRMdCSKWNjk= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09 h1:OoRAFlvDGCUqDLampLQjk0yeeSGdF9zzst/3G9IkBbc= +github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09/go.mod h1:m2r/smMKsKwgMSAoFKHaa68ImdCSNuKE1MxvQ64xuCQ= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= @@ -48,9 +48,9 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg= +github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -87,6 +87,8 @@ github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -125,6 +127,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/.cirrus.yml b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/.cirrus.yml new file mode 100644 index 00000000000..4e900f86d95 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/.cirrus.yml @@ -0,0 +1,10 @@ +freebsd_instance: + image_family: freebsd-13-0 + +task: + name: Test on FreeBSD + install_script: pkg install -y go119 dbus + test_script: | + /usr/local/etc/rc.d/dbus onestart && \ + eval `dbus-launch --sh-syntax` && \ + go119 test -v ./... diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/.golangci.yml b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/.golangci.yml new file mode 100644 index 00000000000..f2d7910d4c2 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/.golangci.yml @@ -0,0 +1,7 @@ +# For documentation, see https://golangci-lint.run/usage/configuration/ + +linters: + enable: + - gofumpt + - unconvert + - unparam diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/README.md b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/README.md index 5c24125838d..5c6b19655c0 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/README.md +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/README.md @@ -23,7 +23,7 @@ go get github.com/godbus/dbus/v5 ### Usage The complete package documentation and some simple examples are available at -[godoc.org](http://godoc.org/github.com/godbus/dbus). Also, the +[pkg.go.dev](https://pkg.go.dev/github.com/godbus/dbus/v5). Also, the [_examples](https://github.com/godbus/dbus/tree/master/_examples) directory gives a short overview over the basic usage. @@ -34,6 +34,7 @@ gives a short overview over the basic usage. - [iwd](https://github.com/shibumi/iwd) go bindings for the internet wireless daemon "iwd". - [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library. - [playerbm](https://github.com/altdesktop/playerbm) a bookmark utility for media players. +- [rpic](https://github.com/stephenhu/rpic) lightweight web app and RESTful API for managing a Raspberry Pi Please note that the API is considered unstable for now and may change without further notice. diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/SECURITY.md b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/SECURITY.md new file mode 100644 index 00000000000..7d262fbbfcf --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +Security updates are applied only to the latest release. + +## Reporting a Vulnerability + +If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. + +Please disclose it at [security advisory](https://github.com/godbus/dbus/security/advisories/new). + +This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base. diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/auth.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/auth.go index 0f3b252c070..5fecbd3d419 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/auth.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/auth.go @@ -83,9 +83,9 @@ func (conn *Conn) Auth(methods []Auth) error { } switch status { case AuthOk: - err, ok = conn.tryAuth(m, waitingForOk, in) + ok, err = conn.tryAuth(m, waitingForOk, in) case AuthContinue: - err, ok = conn.tryAuth(m, waitingForData, in) + ok, err = conn.tryAuth(m, waitingForData, in) default: panic("dbus: invalid authentication status") } @@ -125,21 +125,21 @@ func (conn *Conn) Auth(methods []Auth) error { } // tryAuth tries to authenticate with m as the mechanism, using state as the -// initial authState and in for reading input. It returns (nil, true) on -// success, (nil, false) on a REJECTED and (someErr, false) if some other +// initial authState and in for reading input. It returns (true, nil) on +// success, (false, nil) on a REJECTED and (false, someErr) if some other // error occurred. -func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, bool) { +func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (bool, error) { for { s, err := authReadLine(in) if err != nil { - return err, false + return false, err } switch { case state == waitingForData && string(s[0]) == "DATA": if len(s) != 2 { err = authWriteLine(conn.transport, []byte("ERROR")) if err != nil { - return err, false + return false, err } continue } @@ -149,7 +149,7 @@ func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, boo if len(data) != 0 { err = authWriteLine(conn.transport, []byte("DATA"), data) if err != nil { - return err, false + return false, err } } if status == AuthOk { @@ -158,66 +158,66 @@ func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, boo case AuthError: err = authWriteLine(conn.transport, []byte("ERROR")) if err != nil { - return err, false + return false, err } } case state == waitingForData && string(s[0]) == "REJECTED": - return nil, false + return false, nil case state == waitingForData && string(s[0]) == "ERROR": err = authWriteLine(conn.transport, []byte("CANCEL")) if err != nil { - return err, false + return false, err } state = waitingForReject case state == waitingForData && string(s[0]) == "OK": if len(s) != 2 { err = authWriteLine(conn.transport, []byte("CANCEL")) if err != nil { - return err, false + return false, err } state = waitingForReject } else { conn.uuid = string(s[1]) - return nil, true + return true, nil } case state == waitingForData: err = authWriteLine(conn.transport, []byte("ERROR")) if err != nil { - return err, false + return false, err } case state == waitingForOk && string(s[0]) == "OK": if len(s) != 2 { err = authWriteLine(conn.transport, []byte("CANCEL")) if err != nil { - return err, false + return false, err } state = waitingForReject } else { conn.uuid = string(s[1]) - return nil, true + return true, nil } case state == waitingForOk && string(s[0]) == "DATA": err = authWriteLine(conn.transport, []byte("DATA")) if err != nil { - return err, false + return false, nil } case state == waitingForOk && string(s[0]) == "REJECTED": - return nil, false + return false, nil case state == waitingForOk && string(s[0]) == "ERROR": err = authWriteLine(conn.transport, []byte("CANCEL")) if err != nil { - return err, false + return false, err } state = waitingForReject case state == waitingForOk: err = authWriteLine(conn.transport, []byte("ERROR")) if err != nil { - return err, false + return false, err } case state == waitingForReject && string(s[0]) == "REJECTED": - return nil, false + return false, nil case state == waitingForReject: - return errors.New("dbus: authentication protocol error"), false + return false, errors.New("dbus: authentication protocol error") default: panic("dbus: invalid auth state") } diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/call.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/call.go index b06b063580f..ac01ec669fc 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/call.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/call.go @@ -2,11 +2,8 @@ package dbus import ( "context" - "errors" ) -var errSignature = errors.New("dbus: mismatched signature") - // Call represents a pending or completed method call. type Call struct { Destination string diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn.go index 69978ea26ab..bbe111b2280 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn.go @@ -76,7 +76,6 @@ func SessionBus() (conn *Conn, err error) { func getSessionBusAddress(autolaunch bool) (string, error) { if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" { return address, nil - } else if address := tryDiscoverDbusSessionBusAddress(); address != "" { os.Setenv("DBUS_SESSION_BUS_ADDRESS", address) return address, nil @@ -485,7 +484,7 @@ func (conn *Conn) Object(dest string, path ObjectPath) BusObject { return &Object{conn, dest, path} } -func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) { +func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) error { if msg.serial == 0 { msg.serial = conn.getSerial() } @@ -498,6 +497,7 @@ func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) { } else if msg.Type != TypeMethodCall { conn.serialGen.RetireSerial(msg.serial) } + return err } func (conn *Conn) handleSendError(msg *Message, err error) { @@ -505,6 +505,9 @@ func (conn *Conn) handleSendError(msg *Message, err error) { conn.calls.handleSendError(msg, err) } else if msg.Type == TypeMethodReply { if _, ok := err.(FormatError); ok { + // Make sure that the caller gets some kind of error response if + // the application code tried to respond, but the resulting message + // was malformed in the end conn.sendError(err, msg.Headers[FieldDestination].value.(string), msg.Headers[FieldReplySerial].value.(uint32)) } } @@ -560,7 +563,8 @@ func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call { <-ctx.Done() conn.calls.handleSendError(msg, ctx.Err()) }() - conn.sendMessageAndIfClosed(msg, func() { + // error is handled in handleSendError + _ = conn.sendMessageAndIfClosed(msg, func() { conn.calls.handleSendError(msg, ErrClosed) canceler() }) @@ -568,7 +572,8 @@ func (conn *Conn) send(ctx context.Context, msg *Message, ch chan *Call) *Call { canceler() call = &Call{Err: nil, Done: ch} ch <- call - conn.sendMessageAndIfClosed(msg, func() { + // error is handled in handleSendError + _ = conn.sendMessageAndIfClosed(msg, func() { call = &Call{Err: ErrClosed} }) } @@ -602,7 +607,8 @@ func (conn *Conn) sendError(err error, dest string, serial uint32) { if len(e.Body) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(e.Body...)) } - conn.sendMessageAndIfClosed(msg, nil) + // not much we can do to handle a possible error here + _ = conn.sendMessageAndIfClosed(msg, nil) } // sendReply creates a method reply message corresponding to the parameters and @@ -619,7 +625,8 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { if len(values) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) } - conn.sendMessageAndIfClosed(msg, nil) + // not much we can do to handle a possible error here + _ = conn.sendMessageAndIfClosed(msg, nil) } // AddMatchSignal registers the given match rule to receive broadcast @@ -630,7 +637,7 @@ func (conn *Conn) AddMatchSignal(options ...MatchOption) error { // AddMatchSignalContext acts like AddMatchSignal but takes a context. func (conn *Conn) AddMatchSignalContext(ctx context.Context, options ...MatchOption) error { - options = append([]MatchOption{withMatchType("signal")}, options...) + options = append([]MatchOption{withMatchTypeSignal()}, options...) return conn.busObj.CallWithContext( ctx, "org.freedesktop.DBus.AddMatch", 0, @@ -645,7 +652,7 @@ func (conn *Conn) RemoveMatchSignal(options ...MatchOption) error { // RemoveMatchSignalContext acts like RemoveMatchSignal but takes a context. func (conn *Conn) RemoveMatchSignalContext(ctx context.Context, options ...MatchOption) error { - options = append([]MatchOption{withMatchType("signal")}, options...) + options = append([]MatchOption{withMatchTypeSignal()}, options...) return conn.busObj.CallWithContext( ctx, "org.freedesktop.DBus.RemoveMatch", 0, @@ -740,9 +747,7 @@ type transport interface { SendMessage(*Message) error } -var ( - transports = make(map[string]func(string) (transport, error)) -) +var transports = make(map[string]func(string) (transport, error)) func getTransport(address string) (transport, error) { var err error @@ -853,16 +858,19 @@ type nameTracker struct { func newNameTracker() *nameTracker { return &nameTracker{names: map[string]struct{}{}} } + func (tracker *nameTracker) acquireUniqueConnectionName(name string) { tracker.lck.Lock() defer tracker.lck.Unlock() tracker.unique = name } + func (tracker *nameTracker) acquireName(name string) { tracker.lck.Lock() defer tracker.lck.Unlock() tracker.names[name] = struct{}{} } + func (tracker *nameTracker) loseName(name string) { tracker.lck.Lock() defer tracker.lck.Unlock() @@ -874,12 +882,14 @@ func (tracker *nameTracker) uniqueNameIsKnown() bool { defer tracker.lck.RUnlock() return tracker.unique != "" } + func (tracker *nameTracker) isKnownName(name string) bool { tracker.lck.RLock() defer tracker.lck.RUnlock() _, ok := tracker.names[name] return ok || name == tracker.unique } + func (tracker *nameTracker) listKnownNames() []string { tracker.lck.RLock() defer tracker.lck.RUnlock() @@ -941,17 +951,6 @@ func (tracker *callTracker) handleSendError(msg *Message, err error) { } } -// finalize was the only func that did not strobe Done -func (tracker *callTracker) finalize(sn uint32) { - tracker.lck.Lock() - defer tracker.lck.Unlock() - c, ok := tracker.calls[sn] - if ok { - delete(tracker.calls, sn) - c.ContextCancel() - } -} - func (tracker *callTracker) finalizeWithBody(sn uint32, sequence Sequence, body []interface{}) { tracker.lck.Lock() c, ok := tracker.calls[sn] diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_darwin.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_darwin.go index 6e2e4020216..cb2325a01b4 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_darwin.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_darwin.go @@ -12,7 +12,6 @@ const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_so func getSessionBusPlatformAddress() (string, error) { cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET") b, err := cmd.CombinedOutput() - if err != nil { return "", err } diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_other.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_other.go index 90289ca85a2..067e67cc5bf 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_other.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_other.go @@ -1,3 +1,4 @@ +//go:build !darwin // +build !darwin package dbus @@ -19,7 +20,6 @@ var execCommand = exec.Command func getSessionBusPlatformAddress() (string, error) { cmd := execCommand("dbus-launch") b, err := cmd.CombinedOutput() - if err != nil { return "", err } @@ -42,10 +42,10 @@ func getSessionBusPlatformAddress() (string, error) { // It tries different techniques employed by different operating systems, // returning the first valid address it finds, or an empty string. // -// * /run/user//bus if this exists, it *is* the bus socket. present on -// Ubuntu 18.04 -// * /run/user//dbus-session: if this exists, it can be parsed for the bus -// address. present on Ubuntu 16.04 +// - /run/user//bus if this exists, it *is* the bus socket. present on +// Ubuntu 18.04 +// - /run/user//dbus-session: if this exists, it can be parsed for the bus +// address. present on Ubuntu 16.04 // // See https://dbus.freedesktop.org/doc/dbus-launch.1.html func tryDiscoverDbusSessionBusAddress() string { diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_unix.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_unix.go index 58aee7d2af5..1a0daa6566c 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_unix.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_unix.go @@ -1,4 +1,5 @@ -//+build !windows,!solaris,!darwin +//go:build !windows && !solaris && !darwin +// +build !windows,!solaris,!darwin package dbus diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_windows.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_windows.go index 4291e4519cc..fa839d2a228 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_windows.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/conn_windows.go @@ -1,5 +1,3 @@ -//+build windows - package dbus import "os" diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/dbus.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/dbus.go index c188d104854..8f152dc2f30 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/dbus.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/dbus.go @@ -10,11 +10,8 @@ import ( var ( byteType = reflect.TypeOf(byte(0)) boolType = reflect.TypeOf(false) - uint8Type = reflect.TypeOf(uint8(0)) int16Type = reflect.TypeOf(int16(0)) uint16Type = reflect.TypeOf(uint16(0)) - intType = reflect.TypeOf(int(0)) - uintType = reflect.TypeOf(uint(0)) int32Type = reflect.TypeOf(int32(0)) uint32Type = reflect.TypeOf(uint32(0)) int64Type = reflect.TypeOf(int64(0)) @@ -85,7 +82,7 @@ func storeBase(dest, src reflect.Value) error { func setDest(dest, src reflect.Value) error { if !isVariant(src.Type()) && isVariant(dest.Type()) { - //special conversion for dbus.Variant + // special conversion for dbus.Variant dest.Set(reflect.ValueOf(MakeVariant(src.Interface()))) return nil } @@ -166,8 +163,8 @@ func storeMapIntoVariant(dest, src reflect.Value) error { func storeMapIntoInterface(dest, src reflect.Value) error { var dv reflect.Value if isVariant(src.Type().Elem()) { - //Convert variants to interface{} recursively when converting - //to interface{} + // Convert variants to interface{} recursively when converting + // to interface{} dv = reflect.MakeMap( reflect.MapOf(src.Type().Key(), interfaceType)) } else { @@ -200,7 +197,7 @@ func storeMapIntoMap(dest, src reflect.Value) error { func storeSlice(dest, src reflect.Value) error { switch { case src.Type() == interfacesType && dest.Kind() == reflect.Struct: - //The decoder always decodes structs as slices of interface{} + // The decoder always decodes structs as slices of interface{} return storeStruct(dest, src) case !kindsAreCompatible(dest.Type(), src.Type()): return fmt.Errorf( @@ -260,8 +257,8 @@ func storeSliceIntoVariant(dest, src reflect.Value) error { func storeSliceIntoInterface(dest, src reflect.Value) error { var dv reflect.Value if isVariant(src.Type().Elem()) { - //Convert variants to interface{} recursively when converting - //to interface{} + // Convert variants to interface{} recursively when converting + // to interface{} dv = reflect.MakeSlice(reflect.SliceOf(interfaceType), src.Len(), src.Cap()) } else { @@ -334,7 +331,7 @@ func (o ObjectPath) IsValid() bool { } // A UnixFD is a Unix file descriptor sent over the wire. See the package-level -// documentation for more information about Unix file descriptor passsing. +// documentation for more information about Unix file descriptor passing. type UnixFD int32 // A UnixFDIndex is the representation of a Unix file descriptor in a message. diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/decoder.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/decoder.go index 89bfed9d1a1..97a827b83b2 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/decoder.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/decoder.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "io" "reflect" + "unsafe" ) type decoder struct { @@ -11,6 +12,12 @@ type decoder struct { order binary.ByteOrder pos int fds []int + + // The following fields are used to reduce memory allocs. + conv *stringConverter + buf []byte + d float64 + y [1]byte } // newDecoder returns a new decoder that reads values from in. The input is @@ -20,17 +27,27 @@ func newDecoder(in io.Reader, order binary.ByteOrder, fds []int) *decoder { dec.in = in dec.order = order dec.fds = fds + dec.conv = newStringConverter(stringConverterBufferSize) return dec } +// Reset resets the decoder to be reading from in. +func (dec *decoder) Reset(in io.Reader, order binary.ByteOrder, fds []int) { + dec.in = in + dec.order = order + dec.pos = 0 + dec.fds = fds + + if dec.conv == nil { + dec.conv = newStringConverter(stringConverterBufferSize) + } +} + // align aligns the input to the given boundary and panics on error. func (dec *decoder) align(n int) { if dec.pos%n != 0 { newpos := (dec.pos + n - 1) & ^(n - 1) - empty := make([]byte, newpos-dec.pos) - if _, err := io.ReadFull(dec.in, empty); err != nil { - panic(err) - } + dec.read2buf(newpos - dec.pos) dec.pos = newpos } } @@ -66,79 +83,89 @@ func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) { return vs, nil } +// read2buf reads exactly n bytes from the reader dec.in into the buffer dec.buf +// to reduce memory allocs. +// The buffer grows automatically. +func (dec *decoder) read2buf(n int) { + if cap(dec.buf) < n { + dec.buf = make([]byte, n) + } else { + dec.buf = dec.buf[:n] + } + if _, err := io.ReadFull(dec.in, dec.buf); err != nil { + panic(err) + } +} + +// decodeU decodes uint32 obtained from the reader dec.in. +// The goal is to reduce memory allocs. +func (dec *decoder) decodeU() uint32 { + dec.align(4) + dec.read2buf(4) + dec.pos += 4 + return dec.order.Uint32(dec.buf) +} + func (dec *decoder) decode(s string, depth int) interface{} { dec.align(alignment(typeFor(s))) switch s[0] { case 'y': - var b [1]byte - if _, err := dec.in.Read(b[:]); err != nil { + if _, err := dec.in.Read(dec.y[:]); err != nil { panic(err) } dec.pos++ - return b[0] + return dec.y[0] case 'b': - i := dec.decode("u", depth).(uint32) - switch { - case i == 0: + switch dec.decodeU() { + case 0: return false - case i == 1: + case 1: return true default: panic(FormatError("invalid value for boolean")) } case 'n': - var i int16 - dec.binread(&i) + dec.read2buf(2) dec.pos += 2 - return i + return int16(dec.order.Uint16(dec.buf)) case 'i': - var i int32 - dec.binread(&i) + dec.read2buf(4) dec.pos += 4 - return i + return int32(dec.order.Uint32(dec.buf)) case 'x': - var i int64 - dec.binread(&i) + dec.read2buf(8) dec.pos += 8 - return i + return int64(dec.order.Uint64(dec.buf)) case 'q': - var i uint16 - dec.binread(&i) + dec.read2buf(2) dec.pos += 2 - return i + return dec.order.Uint16(dec.buf) case 'u': - var i uint32 - dec.binread(&i) - dec.pos += 4 - return i + return dec.decodeU() case 't': - var i uint64 - dec.binread(&i) + dec.read2buf(8) dec.pos += 8 - return i + return dec.order.Uint64(dec.buf) case 'd': - var f float64 - dec.binread(&f) + dec.binread(&dec.d) dec.pos += 8 - return f + return dec.d case 's': - length := dec.decode("u", depth).(uint32) - b := make([]byte, int(length)+1) - if _, err := io.ReadFull(dec.in, b); err != nil { - panic(err) - } - dec.pos += int(length) + 1 - return string(b[:len(b)-1]) + length := dec.decodeU() + p := int(length) + 1 + dec.read2buf(p) + dec.pos += p + return dec.conv.String(dec.buf[:len(dec.buf)-1]) case 'o': return ObjectPath(dec.decode("s", depth).(string)) case 'g': length := dec.decode("y", depth).(byte) - b := make([]byte, int(length)+1) - if _, err := io.ReadFull(dec.in, b); err != nil { - panic(err) - } - dec.pos += int(length) + 1 - sig, err := ParseSignature(string(b[:len(b)-1])) + p := int(length) + 1 + dec.read2buf(p) + dec.pos += p + sig, err := ParseSignature( + dec.conv.String(dec.buf[:len(dec.buf)-1]), + ) if err != nil { panic(err) } @@ -163,7 +190,7 @@ func (dec *decoder) decode(s string, depth int) interface{} { variant.value = dec.decode(sig.str, depth+1) return variant case 'h': - idx := dec.decode("u", depth).(uint32) + idx := dec.decodeU() if int(idx) < len(dec.fds) { return UnixFD(dec.fds[idx]) } @@ -176,7 +203,7 @@ func (dec *decoder) decode(s string, depth int) interface{} { if depth >= 63 { panic(FormatError("input exceeds container depth limit")) } - length := dec.decode("u", depth).(uint32) + length := dec.decodeU() // Even for empty maps, the correct padding must be included dec.align(8) spos := dec.pos @@ -195,7 +222,7 @@ func (dec *decoder) decode(s string, depth int) interface{} { panic(FormatError("input exceeds container depth limit")) } sig := s[1:] - length := dec.decode("u", depth).(uint32) + length := dec.decodeU() // capacity can be determined only for fixed-size element types var capacity int if s := sigByteSize(sig); s != 0 { @@ -205,9 +232,9 @@ func (dec *decoder) decode(s string, depth int) interface{} { // Even for empty arrays, the correct padding must be included align := alignment(typeFor(s[1:])) if len(s) > 1 && s[1] == '(' { - //Special case for arrays of structs - //structs decode as a slice of interface{} values - //but the dbus alignment does not match this + // Special case for arrays of structs + // structs decode as a slice of interface{} values + // but the dbus alignment does not match this align = 8 } dec.align(align) @@ -290,3 +317,65 @@ type FormatError string func (e FormatError) Error() string { return "dbus: wire format error: " + string(e) } + +// stringConverterBufferSize defines the recommended buffer size of 4KB. +// It showed good results in a benchmark when decoding 35KB message, +// see https://github.com/marselester/systemd#testing. +const stringConverterBufferSize = 4096 + +func newStringConverter(capacity int) *stringConverter { + return &stringConverter{ + buf: make([]byte, 0, capacity), + offset: 0, + } +} + +// stringConverter converts bytes to strings with less allocs. +// The idea is to accumulate bytes in a buffer with specified capacity +// and create strings with unsafe package using bytes from a buffer. +// For example, 10 "fizz" strings written to a 40-byte buffer +// will result in 1 alloc instead of 10. +// +// Once a buffer is filled, a new one is created with the same capacity. +// Old buffers will be eventually GC-ed +// with no side effects to the returned strings. +type stringConverter struct { + // buf is a temporary buffer where decoded strings are batched. + buf []byte + // offset is a buffer position where the last string was written. + offset int +} + +// String converts bytes to a string. +func (c *stringConverter) String(b []byte) string { + n := len(b) + if n == 0 { + return "" + } + // Must allocate because a string doesn't fit into the buffer. + if n > cap(c.buf) { + return string(b) + } + + if len(c.buf)+n > cap(c.buf) { + c.buf = make([]byte, 0, cap(c.buf)) + c.offset = 0 + } + c.buf = append(c.buf, b...) + + b = c.buf[c.offset:] + s := toString(b) + c.offset += n + return s +} + +// toString converts a byte slice to a string without allocating. +// Starting from Go 1.20 you should use unsafe.String. +func toString(b []byte) string { + var s string + h := (*reflect.StringHeader)(unsafe.Pointer(&s)) + h.Data = uintptr(unsafe.Pointer(&b[0])) + h.Len = len(b) + + return s +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/default_handler.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/default_handler.go index 13132c6b479..3da16b1ecb7 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/default_handler.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/default_handler.go @@ -18,9 +18,9 @@ func newIntrospectIntf(h *defaultHandler) *exportedIntf { return newExportedIntf(methods, true) } -//NewDefaultHandler returns an instance of the default -//call handler. This is useful if you want to implement only -//one of the two handlers but not both. +// NewDefaultHandler returns an instance of the default +// call handler. This is useful if you want to implement only +// one of the two handlers but not both. // // Deprecated: this is the default value, don't use it, it will be unexported. func NewDefaultHandler() *defaultHandler { @@ -148,7 +148,7 @@ func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) { out[i] = val.Interface() } if nilErr || err == nil { - //concrete type to interface nil is a special case + // concrete type to interface nil is a special case return out, nil } return out, err @@ -215,10 +215,6 @@ func (obj *exportedObj) LookupMethod(name string) (Method, bool) { return nil, false } -func (obj *exportedObj) isFallbackInterface() bool { - return false -} - func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf { return &exportedIntf{ methods: methods, @@ -242,9 +238,9 @@ func (obj *exportedIntf) isFallbackInterface() bool { return obj.includeSubtree } -//NewDefaultSignalHandler returns an instance of the default -//signal handler. This is useful if you want to implement only -//one of the two handlers but not both. +// NewDefaultSignalHandler returns an instance of the default +// signal handler. This is useful if you want to implement only +// one of the two handlers but not both. // // Deprecated: this is the default value, don't use it, it will be unexported. func NewDefaultSignalHandler() *defaultSignalHandler { diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/doc.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/doc.go index 8f25a00d61e..09eedc71e61 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/doc.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/doc.go @@ -7,7 +7,7 @@ on remote objects and emit or receive signals. Using the Export method, you can arrange D-Bus methods calls to be directly translated to method calls on a Go value. -Conversion Rules +# Conversion Rules For outgoing messages, Go types are automatically converted to the corresponding D-Bus types. See the official specification at @@ -15,25 +15,25 @@ https://dbus.freedesktop.org/doc/dbus-specification.html#type-system for more information on the D-Bus type system. The following types are directly encoded as their respective D-Bus equivalents: - Go type | D-Bus type - ------------+----------- - byte | BYTE - bool | BOOLEAN - int16 | INT16 - uint16 | UINT16 - int | INT32 - uint | UINT32 - int32 | INT32 - uint32 | UINT32 - int64 | INT64 - uint64 | UINT64 - float64 | DOUBLE - string | STRING - ObjectPath | OBJECT_PATH - Signature | SIGNATURE - Variant | VARIANT - interface{} | VARIANT - UnixFDIndex | UNIX_FD + Go type | D-Bus type + ------------+----------- + byte | BYTE + bool | BOOLEAN + int16 | INT16 + uint16 | UINT16 + int | INT32 + uint | UINT32 + int32 | INT32 + uint32 | UINT32 + int64 | INT64 + uint64 | UINT64 + float64 | DOUBLE + string | STRING + ObjectPath | OBJECT_PATH + Signature | SIGNATURE + Variant | VARIANT + interface{} | VARIANT + UnixFDIndex | UNIX_FD Slices and arrays encode as ARRAYs of their element type. @@ -57,7 +57,7 @@ of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces containing the struct fields in the correct order. The Store function can be used to convert such values to Go structs. -Unix FD passing +# Unix FD passing Handling Unix file descriptors deserves special mention. To use them, you should first check that they are supported on a connection by calling SupportsUnixFDs. @@ -66,6 +66,5 @@ UnixFD's to messages that are accompanied by the given file descriptors with the UnixFD values being substituted by the correct indices. Similarly, the indices of incoming messages are automatically resolved. It shouldn't be necessary to use UnixFDIndex. - */ package dbus diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/export.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/export.go index d3dd9f7cd62..fa009d2ce41 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/export.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/export.go @@ -205,15 +205,13 @@ func (conn *Conn) handleCall(msg *Message) { } reply.Headers[FieldReplySerial] = MakeVariant(msg.serial) reply.Body = make([]interface{}, len(ret)) - for i := 0; i < len(ret); i++ { - reply.Body[i] = ret[i] - } + copy(reply.Body, ret) reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) - if err := reply.IsValid(); err != nil { - fmt.Fprintf(os.Stderr, "dbus: dropping invalid reply to %s.%s on obj %s: %s\n", ifaceName, name, path, err) - } else { - conn.sendMessageAndIfClosed(reply, nil) + if err := conn.sendMessageAndIfClosed(reply, nil); err != nil { + if _, ok := err.(FormatError); ok { + fmt.Fprintf(os.Stderr, "dbus: replacing invalid reply to %s.%s on obj %s: %s\n", ifaceName, name, path, err) + } } } } @@ -237,18 +235,15 @@ func (conn *Conn) Emit(path ObjectPath, name string, values ...interface{}) erro if len(values) > 0 { msg.Headers[FieldSignature] = MakeVariant(SignatureOf(values...)) } - if err := msg.IsValid(); err != nil { - return err - } var closed bool - conn.sendMessageAndIfClosed(msg, func() { + err := conn.sendMessageAndIfClosed(msg, func() { closed = true }) if closed { return ErrClosed } - return nil + return err } // Export registers the given value to be exported as an object on the diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/match.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/match.go index 5a607e53e41..ffb01344755 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/match.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/match.go @@ -26,10 +26,10 @@ func WithMatchOption(key, value string) MatchOption { return MatchOption{key, value} } -// doesn't make sense to export this option because clients can only -// subscribe to messages with signal type. -func withMatchType(typ string) MatchOption { - return WithMatchOption("type", typ) +// It does not make sense to have a public WithMatchType function +// because clients can only subscribe to messages with signal type. +func withMatchTypeSignal() MatchOption { + return WithMatchOption("type", "signal") } // WithMatchSender sets sender match option. diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/message.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/message.go index bdf43fdd6e5..5ab6e9d9a15 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/message.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/message.go @@ -158,7 +158,9 @@ func DecodeMessageWithFDs(rd io.Reader, fds []int) (msg *Message, err error) { if err != nil { return nil, err } - binary.Read(bytes.NewBuffer(b), order, &hlength) + if err := binary.Read(bytes.NewBuffer(b), order, &hlength); err != nil { + return nil, err + } if hlength+length+16 > 1<<27 { return nil, InvalidMessageError("message is too long") } @@ -186,7 +188,7 @@ func DecodeMessageWithFDs(rd io.Reader, fds []int) (msg *Message, err error) { } } - if err = msg.IsValid(); err != nil { + if err = msg.validateHeader(); err != nil { return nil, err } sig, _ := msg.Headers[FieldSignature].value.(Signature) @@ -265,12 +267,14 @@ func (msg *Message) EncodeToWithFDs(out io.Writer, order binary.ByteOrder) (fds return } enc.align(8) - body.WriteTo(&buf) + if _, err := body.WriteTo(&buf); err != nil { + return nil, err + } if buf.Len() > 1<<27 { - return make([]int, 0), InvalidMessageError("message is too long") + return nil, InvalidMessageError("message is too long") } if _, err := buf.WriteTo(out); err != nil { - return make([]int, 0), err + return nil, err } return enc.fds, nil } @@ -286,8 +290,7 @@ func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) (err error) // IsValid checks whether msg is a valid message and returns an // InvalidMessageError or FormatError if it is not. func (msg *Message) IsValid() error { - var b bytes.Buffer - return msg.EncodeTo(&b, nativeEndian) + return msg.EncodeTo(io.Discard, nativeEndian) } func (msg *Message) validateHeader() error { diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/object.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/object.go index 664abb7fbab..b4b1e939b39 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/object.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/object.go @@ -46,7 +46,7 @@ func (o *Object) CallWithContext(ctx context.Context, method string, flags Flags // Deprecated: use (*Conn) AddMatchSignal instead. func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *Call { base := []MatchOption{ - withMatchType("signal"), + withMatchTypeSignal(), WithMatchInterface(iface), WithMatchMember(member), } @@ -65,7 +65,7 @@ func (o *Object) AddMatchSignal(iface, member string, options ...MatchOption) *C // Deprecated: use (*Conn) RemoveMatchSignal instead. func (o *Object) RemoveMatchSignal(iface, member string, options ...MatchOption) *Call { base := []MatchOption{ - withMatchType("signal"), + withMatchTypeSignal(), WithMatchInterface(iface), WithMatchMember(member), } @@ -151,7 +151,14 @@ func (o *Object) StoreProperty(p string, value interface{}) error { // SetProperty calls org.freedesktop.DBus.Properties.Set on the given // object. The property name must be given in interface.member notation. +// Panics if v is not a valid Variant type. func (o *Object) SetProperty(p string, v interface{}) error { + // v might already be a variant... + variant, ok := v.(Variant) + if !ok { + // Otherwise, make it into one. + variant = MakeVariant(v) + } idx := strings.LastIndex(p, ".") if idx == -1 || idx+1 == len(p) { return errors.New("dbus: invalid property " + p) @@ -160,7 +167,7 @@ func (o *Object) SetProperty(p string, v interface{}) error { iface := p[:idx] prop := p[idx+1:] - return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, v).Err + return o.Call("org.freedesktop.DBus.Properties.Set", 0, iface, prop, variant).Err } // Destination returns the destination that calls on (o *Object) are sent to. diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/sequential_handler.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/sequential_handler.go index ef2fcdba179..886b5eb16b3 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/sequential_handler.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/sequential_handler.go @@ -93,7 +93,7 @@ func (scd *sequentialSignalChannelData) bufferSignals() { var queue []*Signal for { if len(queue) == 0 { - signal, ok := <- scd.in + signal, ok := <-scd.in if !ok { return } diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/server_interfaces.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/server_interfaces.go index e4e0389fdf0..aef3c772fa0 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/server_interfaces.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/server_interfaces.go @@ -22,7 +22,7 @@ type Handler interface { // of Interface lookup is up to the implementation of // the ServerObject. The ServerObject implementation may // choose to implement empty string as a valid interface -// represeting all methods or not per the D-Bus specification. +// representing all methods or not per the D-Bus specification. type ServerObject interface { LookupInterface(name string) (Interface, bool) } diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/sig.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/sig.go index 6b9cadb5fbc..5bd797b62fc 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/sig.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/sig.go @@ -183,19 +183,19 @@ func (cnt *depthCounter) Valid() bool { return cnt.arrayDepth <= 32 && cnt.structDepth <= 32 && cnt.dictEntryDepth <= 32 } -func (cnt depthCounter) EnterArray() *depthCounter { +func (cnt *depthCounter) EnterArray() *depthCounter { cnt.arrayDepth++ - return &cnt + return cnt } -func (cnt depthCounter) EnterStruct() *depthCounter { +func (cnt *depthCounter) EnterStruct() *depthCounter { cnt.structDepth++ - return &cnt + return cnt } -func (cnt depthCounter) EnterDictEntry() *depthCounter { +func (cnt *depthCounter) EnterDictEntry() *depthCounter { cnt.dictEntryDepth++ - return &cnt + return cnt } // Try to read a single type from this string. If it was successful, err is nil @@ -221,6 +221,9 @@ func validSingle(s string, depth *depthCounter) (err error, rem string) { i++ rem = s[i+1:] s = s[2:i] + if len(s) == 0 { + return SignatureError{Sig: s, Reason: "empty dict"}, "" + } if err, _ = validSingle(s[:1], depth.EnterArray().EnterDictEntry()); err != nil { return err, "" } diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go index 697739efafb..a61a82084bd 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go @@ -1,4 +1,5 @@ -//+build !windows +//go:build !windows +// +build !windows package dbus diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_unix.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_unix.go index 0a8c712ebdf..6840387a5f5 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_unix.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_unix.go @@ -1,4 +1,5 @@ -//+build !windows,!solaris +//go:build !windows && !solaris +// +build !windows,!solaris package dbus @@ -11,10 +12,29 @@ import ( "syscall" ) +// msghead represents the part of the message header +// that has a constant size (byte order + 15 bytes). +type msghead struct { + Type Type + Flags Flags + Proto byte + BodyLen uint32 + Serial uint32 + HeaderLen uint32 +} + type oobReader struct { conn *net.UnixConn oob []byte buf [4096]byte + + // The following fields are used to reduce memory allocs. + headers []header + csheader []byte + b *bytes.Buffer + r *bytes.Reader + dec *decoder + msghead } func (o *oobReader) Read(b []byte) (n int, err error) { @@ -70,28 +90,36 @@ func (t *unixTransport) EnableUnixFDs() { } func (t *unixTransport) ReadMessage() (*Message, error) { - var ( - blen, hlen uint32 - csheader [16]byte - headers []header - order binary.ByteOrder - unixfds uint32 - ) // To be sure that all bytes of out-of-band data are read, we use a special // reader that uses ReadUnix on the underlying connection instead of Read // and gathers the out-of-band data in a buffer. if t.rdr == nil { - t.rdr = &oobReader{conn: t.UnixConn} + t.rdr = &oobReader{ + conn: t.UnixConn, + // This buffer is used to decode the part of the header that has a constant size. + csheader: make([]byte, 16), + b: &bytes.Buffer{}, + // The reader helps to read from the buffer several times. + r: &bytes.Reader{}, + dec: &decoder{}, + } } else { - t.rdr.oob = nil + t.rdr.oob = t.rdr.oob[:0] + t.rdr.headers = t.rdr.headers[:0] } + var ( + r = t.rdr.r + b = t.rdr.b + dec = t.rdr.dec + ) - // read the first 16 bytes (the part of the header that has a constant size), - // from which we can figure out the length of the rest of the message - if _, err := io.ReadFull(t.rdr, csheader[:]); err != nil { + _, err := io.ReadFull(t.rdr, t.rdr.csheader) + if err != nil { return nil, err } - switch csheader[0] { + + var order binary.ByteOrder + switch t.rdr.csheader[0] { case 'l': order = binary.LittleEndian case 'B': @@ -99,38 +127,62 @@ func (t *unixTransport) ReadMessage() (*Message, error) { default: return nil, InvalidMessageError("invalid byte order") } - // csheader[4:8] -> length of message body, csheader[12:16] -> length of - // header fields (without alignment) - binary.Read(bytes.NewBuffer(csheader[4:8]), order, &blen) - binary.Read(bytes.NewBuffer(csheader[12:]), order, &hlen) + + r.Reset(t.rdr.csheader[1:]) + if err := binary.Read(r, order, &t.rdr.msghead); err != nil { + return nil, err + } + + msg := &Message{ + Type: t.rdr.msghead.Type, + Flags: t.rdr.msghead.Flags, + serial: t.rdr.msghead.Serial, + } + // Length of header fields (without alignment). + hlen := t.rdr.msghead.HeaderLen if hlen%8 != 0 { hlen += 8 - (hlen % 8) } + if hlen+t.rdr.msghead.BodyLen+16 > 1<<27 { + return nil, InvalidMessageError("message is too long") + } - // decode headers and look for unix fds - headerdata := make([]byte, hlen+4) - copy(headerdata, csheader[12:]) - if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil { + // Decode headers and look for unix fds. + b.Reset() + if _, err = b.Write(t.rdr.csheader[12:]); err != nil { + return nil, err + } + if _, err = io.CopyN(b, t.rdr, int64(hlen)); err != nil { return nil, err } - dec := newDecoder(bytes.NewBuffer(headerdata), order, make([]int, 0)) + dec.Reset(b, order, nil) dec.pos = 12 vs, err := dec.Decode(Signature{"a(yv)"}) if err != nil { return nil, err } - Store(vs, &headers) - for _, v := range headers { + if err = Store(vs, &t.rdr.headers); err != nil { + return nil, err + } + var unixfds uint32 + for _, v := range t.rdr.headers { if v.Field == byte(FieldUnixFDs) { unixfds, _ = v.Variant.value.(uint32) } } - all := make([]byte, 16+hlen+blen) - copy(all, csheader[:]) - copy(all[16:], headerdata[4:]) - if _, err := io.ReadFull(t.rdr, all[16+hlen:]); err != nil { + + msg.Headers = make(map[HeaderField]Variant) + for _, v := range t.rdr.headers { + msg.Headers[HeaderField(v.Field)] = v.Variant + } + + dec.align(8) + body := make([]byte, t.rdr.BodyLen) + if _, err = io.ReadFull(t.rdr, body); err != nil { return nil, err } + r.Reset(body) + if unixfds != 0 { if !t.hasUnixFDs { return nil, errors.New("dbus: got unix fds on unsupported transport") @@ -147,8 +199,8 @@ func (t *unixTransport) ReadMessage() (*Message, error) { if err != nil { return nil, err } - msg, err := DecodeMessageWithFDs(bytes.NewBuffer(all), fds) - if err != nil { + dec.Reset(r, order, fds) + if err = decodeMessageBody(msg, dec); err != nil { return nil, err } // substitute the values in the message body (which are indices for the @@ -173,7 +225,27 @@ func (t *unixTransport) ReadMessage() (*Message, error) { } return msg, nil } - return DecodeMessage(bytes.NewBuffer(all)) + + dec.Reset(r, order, nil) + if err = decodeMessageBody(msg, dec); err != nil { + return nil, err + } + return msg, nil +} + +func decodeMessageBody(msg *Message, dec *decoder) error { + if err := msg.validateHeader(); err != nil { + return err + } + + sig, _ := msg.Headers[FieldSignature].value.(Signature) + if sig.str == "" { + return nil + } + + var err error + msg.Body, err = dec.Decode(sig) + return err } func (t *unixTransport) SendMessage(msg *Message) error { diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go index 1b5ed2089d0..ff2284c8382 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go @@ -7,39 +7,41 @@ package dbus -/* -const int sizeofPtr = sizeof(void*); -#define _WANT_UCRED -#include -#include -*/ -import "C" - import ( "io" "os" "syscall" "unsafe" + + "golang.org/x/sys/unix" ) // http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go // https://golang.org/src/syscall/ztypes_freebsd_amd64.go +// +// Note: FreeBSD actually uses a 'struct cmsgcred' which starts with +// these fields and adds a list of the additional groups for the +// sender. type Ucred struct { - Pid int32 - Uid uint32 - Gid uint32 + Pid int32 + Uid uint32 + Euid uint32 + Gid uint32 } -// http://golang.org/src/pkg/syscall/types_linux.go -// https://golang.org/src/syscall/types_freebsd.go -// https://github.com/freebsd/freebsd/blob/master/sys/sys/ucred.h +// https://github.com/freebsd/freebsd/blob/master/sys/sys/socket.h +// +// The cmsgcred structure contains the above four fields, followed by +// a uint16 count of additional groups, uint16 padding to align and a +// 16 element array of uint32 for the additional groups. The size is +// the same across all supported platforms. const ( - SizeofUcred = C.sizeof_struct_ucred + SizeofCmsgcred = 84 // 4*4 + 2*2 + 16*4 ) // http://golang.org/src/pkg/syscall/sockcmsg_unix.go func cmsgAlignOf(salen int) int { - salign := C.sizeofPtr + salign := unix.SizeofPtr return (salen + salign - 1) & ^(salign - 1) } @@ -54,11 +56,11 @@ func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer { // for sending to another process. This can be used for // authentication. func UnixCredentials(ucred *Ucred) []byte { - b := make([]byte, syscall.CmsgSpace(SizeofUcred)) + b := make([]byte, syscall.CmsgSpace(SizeofCmsgcred)) h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0])) h.Level = syscall.SOL_SOCKET h.Type = syscall.SCM_CREDS - h.SetLen(syscall.CmsgLen(SizeofUcred)) + h.SetLen(syscall.CmsgLen(SizeofCmsgcred)) *((*Ucred)(cmsgData(h))) = *ucred return b } diff --git a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/variant_parser.go b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/variant_parser.go index d20f5da6dd2..9532e36f3cc 100644 --- a/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/variant_parser.go +++ b/contrib/tetragon-rthooks/vendor/github.com/godbus/dbus/v5/variant_parser.go @@ -417,7 +417,6 @@ func (b boolNode) Value(sig Signature) (interface{}, error) { type arrayNode struct { set sigSet children []varNode - val interface{} } func (n arrayNode) Infer() (Signature, error) { @@ -574,7 +573,6 @@ type dictEntry struct { type dictNode struct { kset, vset sigSet children []dictEntry - val interface{} } func (n dictNode) Infer() (Signature, error) { diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/LICENSE b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/LICENSE new file mode 100644 index 00000000000..991e2ae966e --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +go-toml v2 +Copyright (c) 2021 - 2023 Thomas Pelletier + +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. diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go new file mode 100644 index 00000000000..80f698db4b0 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go @@ -0,0 +1,42 @@ +package characters + +var invalidAsciiTable = [256]bool{ + 0x00: true, + 0x01: true, + 0x02: true, + 0x03: true, + 0x04: true, + 0x05: true, + 0x06: true, + 0x07: true, + 0x08: true, + // 0x09 TAB + // 0x0A LF + 0x0B: true, + 0x0C: true, + // 0x0D CR + 0x0E: true, + 0x0F: true, + 0x10: true, + 0x11: true, + 0x12: true, + 0x13: true, + 0x14: true, + 0x15: true, + 0x16: true, + 0x17: true, + 0x18: true, + 0x19: true, + 0x1A: true, + 0x1B: true, + 0x1C: true, + 0x1D: true, + 0x1E: true, + 0x1F: true, + // 0x20 - 0x7E Printable ASCII characters + 0x7F: true, +} + +func InvalidAscii(b byte) bool { + return invalidAsciiTable[b] +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go new file mode 100644 index 00000000000..db4f45acbf0 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go @@ -0,0 +1,199 @@ +package characters + +import ( + "unicode/utf8" +) + +type utf8Err struct { + Index int + Size int +} + +func (u utf8Err) Zero() bool { + return u.Size == 0 +} + +// Verified that a given string is only made of valid UTF-8 characters allowed +// by the TOML spec: +// +// Any Unicode character may be used except those that must be escaped: +// quotation mark, backslash, and the control characters other than tab (U+0000 +// to U+0008, U+000A to U+001F, U+007F). +// +// It is a copy of the Go 1.17 utf8.Valid implementation, tweaked to exit early +// when a character is not allowed. +// +// The returned utf8Err is Zero() if the string is valid, or contains the byte +// index and size of the invalid character. +// +// quotation mark => already checked +// backslash => already checked +// 0-0x8 => invalid +// 0x9 => tab, ok +// 0xA - 0x1F => invalid +// 0x7F => invalid +func Utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { + // Fast path. Check for and skip 8 bytes of ASCII characters per iteration. + offset := 0 + for len(p) >= 8 { + // Combining two 32 bit loads allows the same code to be used + // for 32 and 64 bit platforms. + // The compiler can generate a 32bit load for first32 and second32 + // on many platforms. See test/codegen/memcombine.go. + first32 := uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 + second32 := uint32(p[4]) | uint32(p[5])<<8 | uint32(p[6])<<16 | uint32(p[7])<<24 + if (first32|second32)&0x80808080 != 0 { + // Found a non ASCII byte (>= RuneSelf). + break + } + + for i, b := range p[:8] { + if InvalidAscii(b) { + err.Index = offset + i + err.Size = 1 + return + } + } + + p = p[8:] + offset += 8 + } + n := len(p) + for i := 0; i < n; { + pi := p[i] + if pi < utf8.RuneSelf { + if InvalidAscii(pi) { + err.Index = offset + i + err.Size = 1 + return + } + i++ + continue + } + x := first[pi] + if x == xx { + // Illegal starter byte. + err.Index = offset + i + err.Size = 1 + return + } + size := int(x & 7) + if i+size > n { + // Short or invalid. + err.Index = offset + i + err.Size = n - i + return + } + accept := acceptRanges[x>>4] + if c := p[i+1]; c < accept.lo || accept.hi < c { + err.Index = offset + i + err.Size = 2 + return + } else if size == 2 { + } else if c := p[i+2]; c < locb || hicb < c { + err.Index = offset + i + err.Size = 3 + return + } else if size == 3 { + } else if c := p[i+3]; c < locb || hicb < c { + err.Index = offset + i + err.Size = 4 + return + } + i += size + } + return +} + +// Return the size of the next rune if valid, 0 otherwise. +func Utf8ValidNext(p []byte) int { + c := p[0] + + if c < utf8.RuneSelf { + if InvalidAscii(c) { + return 0 + } + return 1 + } + + x := first[c] + if x == xx { + // Illegal starter byte. + return 0 + } + size := int(x & 7) + if size > len(p) { + // Short or invalid. + return 0 + } + accept := acceptRanges[x>>4] + if c := p[1]; c < accept.lo || accept.hi < c { + return 0 + } else if size == 2 { + } else if c := p[2]; c < locb || hicb < c { + return 0 + } else if size == 3 { + } else if c := p[3]; c < locb || hicb < c { + return 0 + } + + return size +} + +// acceptRange gives the range of valid values for the second byte in a UTF-8 +// sequence. +type acceptRange struct { + lo uint8 // lowest value for second byte. + hi uint8 // highest value for second byte. +} + +// acceptRanges has size 16 to avoid bounds checks in the code that uses it. +var acceptRanges = [16]acceptRange{ + 0: {locb, hicb}, + 1: {0xA0, hicb}, + 2: {locb, 0x9F}, + 3: {0x90, hicb}, + 4: {locb, 0x8F}, +} + +// first is information about the first byte in a UTF-8 sequence. +var first = [256]uint8{ + // 1 2 3 4 5 6 7 8 9 A B C D E F + as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F + as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F + as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F + as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F + as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F + as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F + as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F + as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F + // 1 2 3 4 5 6 7 8 9 A B C D E F + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF + xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF + xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF + s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF + s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF + s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF +} + +const ( + // The default lowest and highest continuation byte. + locb = 0b10000000 + hicb = 0b10111111 + + // These names of these constants are chosen to give nice alignment in the + // table below. The first nibble is an index into acceptRanges or F for + // special one-byte cases. The second nibble is the Rune length or the + // Status for the special one-byte case. + xx = 0xF1 // invalid: size 1 + as = 0xF0 // ASCII: size 1 + s1 = 0x02 // accept 0, size 2 + s2 = 0x13 // accept 1, size 3 + s3 = 0x03 // accept 0, size 3 + s4 = 0x23 // accept 2, size 3 + s5 = 0x34 // accept 3, size 4 + s6 = 0x04 // accept 0, size 4 + s7 = 0x44 // accept 4, size 4 +) diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go new file mode 100644 index 00000000000..e38e1131b88 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go @@ -0,0 +1,65 @@ +package danger + +import ( + "fmt" + "reflect" + "unsafe" +) + +const maxInt = uintptr(int(^uint(0) >> 1)) + +func SubsliceOffset(data []byte, subslice []byte) int { + datap := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + hlp := (*reflect.SliceHeader)(unsafe.Pointer(&subslice)) + + if hlp.Data < datap.Data { + panic(fmt.Errorf("subslice address (%d) is before data address (%d)", hlp.Data, datap.Data)) + } + offset := hlp.Data - datap.Data + + if offset > maxInt { + panic(fmt.Errorf("slice offset larger than int (%d)", offset)) + } + + intoffset := int(offset) + + if intoffset > datap.Len { + panic(fmt.Errorf("slice offset (%d) is farther than data length (%d)", intoffset, datap.Len)) + } + + if intoffset+hlp.Len > datap.Len { + panic(fmt.Errorf("slice ends (%d+%d) is farther than data length (%d)", intoffset, hlp.Len, datap.Len)) + } + + return intoffset +} + +func BytesRange(start []byte, end []byte) []byte { + if start == nil || end == nil { + panic("cannot call BytesRange with nil") + } + startp := (*reflect.SliceHeader)(unsafe.Pointer(&start)) + endp := (*reflect.SliceHeader)(unsafe.Pointer(&end)) + + if startp.Data > endp.Data { + panic(fmt.Errorf("start pointer address (%d) is after end pointer address (%d)", startp.Data, endp.Data)) + } + + l := startp.Len + endLen := int(endp.Data-startp.Data) + endp.Len + if endLen > l { + l = endLen + } + + if l > startp.Cap { + panic(fmt.Errorf("range length is larger than capacity")) + } + + return start[:l] +} + +func Stride(ptr unsafe.Pointer, size uintptr, offset int) unsafe.Pointer { + // TODO: replace with unsafe.Add when Go 1.17 is released + // https://github.com/golang/go/issues/40481 + return unsafe.Pointer(uintptr(ptr) + uintptr(int(size)*offset)) +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go new file mode 100644 index 00000000000..9d41c28a2f2 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go @@ -0,0 +1,23 @@ +package danger + +import ( + "reflect" + "unsafe" +) + +// typeID is used as key in encoder and decoder caches to enable using +// the optimize runtime.mapaccess2_fast64 function instead of the more +// expensive lookup if we were to use reflect.Type as map key. +// +// typeID holds the pointer to the reflect.Type value, which is unique +// in the program. +// +// https://github.com/segmentio/encoding/blob/master/json/codec.go#L59-L61 +type TypeID unsafe.Pointer + +func MakeTypeID(t reflect.Type) TypeID { + // reflect.Type has the fields: + // typ unsafe.Pointer + // ptr unsafe.Pointer + return TypeID((*[2]unsafe.Pointer)(unsafe.Pointer(&t))[1]) +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go new file mode 100644 index 00000000000..f526bf2c090 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/ast.go @@ -0,0 +1,136 @@ +package unstable + +import ( + "fmt" + "unsafe" + + "github.com/pelletier/go-toml/v2/internal/danger" +) + +// Iterator over a sequence of nodes. +// +// Starts uninitialized, you need to call Next() first. +// +// For example: +// +// it := n.Children() +// for it.Next() { +// n := it.Node() +// // do something with n +// } +type Iterator struct { + started bool + node *Node +} + +// Next moves the iterator forward and returns true if points to a +// node, false otherwise. +func (c *Iterator) Next() bool { + if !c.started { + c.started = true + } else if c.node.Valid() { + c.node = c.node.Next() + } + return c.node.Valid() +} + +// IsLast returns true if the current node of the iterator is the last +// one. Subsequent calls to Next() will return false. +func (c *Iterator) IsLast() bool { + return c.node.next == 0 +} + +// Node returns a pointer to the node pointed at by the iterator. +func (c *Iterator) Node() *Node { + return c.node +} + +// Node in a TOML expression AST. +// +// Depending on Kind, its sequence of children should be interpreted +// differently. +// +// - Array have one child per element in the array. +// - InlineTable have one child per key-value in the table (each of kind +// InlineTable). +// - KeyValue have at least two children. The first one is the value. The rest +// make a potentially dotted key. +// - Table and ArrayTable's children represent a dotted key (same as +// KeyValue, but without the first node being the value). +// +// When relevant, Raw describes the range of bytes this node is referring to in +// the input document. Use Parser.Raw() to retrieve the actual bytes. +type Node struct { + Kind Kind + Raw Range // Raw bytes from the input. + Data []byte // Node value (either allocated or referencing the input). + + // References to other nodes, as offsets in the backing array + // from this node. References can go backward, so those can be + // negative. + next int // 0 if last element + child int // 0 if no child +} + +// Range of bytes in the document. +type Range struct { + Offset uint32 + Length uint32 +} + +// Next returns a pointer to the next node, or nil if there is no next node. +func (n *Node) Next() *Node { + if n.next == 0 { + return nil + } + ptr := unsafe.Pointer(n) + size := unsafe.Sizeof(Node{}) + return (*Node)(danger.Stride(ptr, size, n.next)) +} + +// Child returns a pointer to the first child node of this node. Other children +// can be accessed calling Next on the first child. Returns an nil if this Node +// has no child. +func (n *Node) Child() *Node { + if n.child == 0 { + return nil + } + ptr := unsafe.Pointer(n) + size := unsafe.Sizeof(Node{}) + return (*Node)(danger.Stride(ptr, size, n.child)) +} + +// Valid returns true if the node's kind is set (not to Invalid). +func (n *Node) Valid() bool { + return n != nil +} + +// Key returns the children nodes making the Key on a supported node. Panics +// otherwise. They are guaranteed to be all be of the Kind Key. A simple key +// would return just one element. +func (n *Node) Key() Iterator { + switch n.Kind { + case KeyValue: + value := n.Child() + if !value.Valid() { + panic(fmt.Errorf("KeyValue should have at least two children")) + } + return Iterator{node: value.Next()} + case Table, ArrayTable: + return Iterator{node: n.Child()} + default: + panic(fmt.Errorf("Key() is not supported on a %s", n.Kind)) + } +} + +// Value returns a pointer to the value node of a KeyValue. +// Guaranteed to be non-nil. Panics if not called on a KeyValue node, +// or if the Children are malformed. +func (n *Node) Value() *Node { + return n.Child() +} + +// Children returns an iterator over a node's children. +func (n *Node) Children() Iterator { + return Iterator{node: n.Child()} +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go new file mode 100644 index 00000000000..9538e30df93 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/builder.go @@ -0,0 +1,71 @@ +package unstable + +// root contains a full AST. +// +// It is immutable once constructed with Builder. +type root struct { + nodes []Node +} + +// Iterator over the top level nodes. +func (r *root) Iterator() Iterator { + it := Iterator{} + if len(r.nodes) > 0 { + it.node = &r.nodes[0] + } + return it +} + +func (r *root) at(idx reference) *Node { + return &r.nodes[idx] +} + +type reference int + +const invalidReference reference = -1 + +func (r reference) Valid() bool { + return r != invalidReference +} + +type builder struct { + tree root + lastIdx int +} + +func (b *builder) Tree() *root { + return &b.tree +} + +func (b *builder) NodeAt(ref reference) *Node { + return b.tree.at(ref) +} + +func (b *builder) Reset() { + b.tree.nodes = b.tree.nodes[:0] + b.lastIdx = 0 +} + +func (b *builder) Push(n Node) reference { + b.lastIdx = len(b.tree.nodes) + b.tree.nodes = append(b.tree.nodes, n) + return reference(b.lastIdx) +} + +func (b *builder) PushAndChain(n Node) reference { + newIdx := len(b.tree.nodes) + b.tree.nodes = append(b.tree.nodes, n) + if b.lastIdx >= 0 { + b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx + } + b.lastIdx = newIdx + return reference(b.lastIdx) +} + +func (b *builder) AttachChild(parent reference, child reference) { + b.tree.nodes[parent].child = int(child) - int(parent) +} + +func (b *builder) Chain(from reference, to reference) { + b.tree.nodes[from].next = int(to) - int(from) +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go new file mode 100644 index 00000000000..7ff26c53c7b --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/doc.go @@ -0,0 +1,3 @@ +// Package unstable provides APIs that do not meet the backward compatibility +// guarantees yet. +package unstable diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go new file mode 100644 index 00000000000..ff9df1bef84 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/kind.go @@ -0,0 +1,71 @@ +package unstable + +import "fmt" + +// Kind represents the type of TOML structure contained in a given Node. +type Kind int + +const ( + // Meta + Invalid Kind = iota + Comment + Key + + // Top level structures + Table + ArrayTable + KeyValue + + // Containers values + Array + InlineTable + + // Values + String + Bool + Float + Integer + LocalDate + LocalTime + LocalDateTime + DateTime +) + +// String implementation of fmt.Stringer. +func (k Kind) String() string { + switch k { + case Invalid: + return "Invalid" + case Comment: + return "Comment" + case Key: + return "Key" + case Table: + return "Table" + case ArrayTable: + return "ArrayTable" + case KeyValue: + return "KeyValue" + case Array: + return "Array" + case InlineTable: + return "InlineTable" + case String: + return "String" + case Bool: + return "Bool" + case Float: + return "Float" + case Integer: + return "Integer" + case LocalDate: + return "LocalDate" + case LocalTime: + return "LocalTime" + case LocalDateTime: + return "LocalDateTime" + case DateTime: + return "DateTime" + } + panic(fmt.Errorf("Kind.String() not implemented for '%d'", k)) +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go new file mode 100644 index 00000000000..50358a44ffd --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/parser.go @@ -0,0 +1,1245 @@ +package unstable + +import ( + "bytes" + "fmt" + "unicode" + + "github.com/pelletier/go-toml/v2/internal/characters" + "github.com/pelletier/go-toml/v2/internal/danger" +) + +// ParserError describes an error relative to the content of the document. +// +// It cannot outlive the instance of Parser it refers to, and may cause panics +// if the parser is reset. +type ParserError struct { + Highlight []byte + Message string + Key []string // optional +} + +// Error is the implementation of the error interface. +func (e *ParserError) Error() string { + return e.Message +} + +// NewParserError is a convenience function to create a ParserError +// +// Warning: Highlight needs to be a subslice of Parser.data, so only slices +// returned by Parser.Raw are valid candidates. +func NewParserError(highlight []byte, format string, args ...interface{}) error { + return &ParserError{ + Highlight: highlight, + Message: fmt.Errorf(format, args...).Error(), + } +} + +// Parser scans over a TOML-encoded document and generates an iterative AST. +// +// To prime the Parser, first reset it with the contents of a TOML document. +// Then, process all top-level expressions sequentially. See Example. +// +// Don't forget to check Error() after you're done parsing. +// +// Each top-level expression needs to be fully processed before calling +// NextExpression() again. Otherwise, calls to various Node methods may panic if +// the parser has moved on the next expression. +// +// For performance reasons, go-toml doesn't make a copy of the input bytes to +// the parser. Make sure to copy all the bytes you need to outlive the slice +// given to the parser. +type Parser struct { + data []byte + builder builder + ref reference + left []byte + err error + first bool + + KeepComments bool +} + +// Data returns the slice provided to the last call to Reset. +func (p *Parser) Data() []byte { + return p.data +} + +// Range returns a range description that corresponds to a given slice of the +// input. If the argument is not a subslice of the parser input, this function +// panics. +func (p *Parser) Range(b []byte) Range { + return Range{ + Offset: uint32(danger.SubsliceOffset(p.data, b)), + Length: uint32(len(b)), + } +} + +// Raw returns the slice corresponding to the bytes in the given range. +func (p *Parser) Raw(raw Range) []byte { + return p.data[raw.Offset : raw.Offset+raw.Length] +} + +// Reset brings the parser to its initial state for a given input. It wipes an +// reuses internal storage to reduce allocation. +func (p *Parser) Reset(b []byte) { + p.builder.Reset() + p.ref = invalidReference + p.data = b + p.left = b + p.err = nil + p.first = true +} + +// NextExpression parses the next top-level expression. If an expression was +// successfully parsed, it returns true. If the parser is at the end of the +// document or an error occurred, it returns false. +// +// Retrieve the parsed expression with Expression(). +func (p *Parser) NextExpression() bool { + if len(p.left) == 0 || p.err != nil { + return false + } + + p.builder.Reset() + p.ref = invalidReference + + for { + if len(p.left) == 0 || p.err != nil { + return false + } + + if !p.first { + p.left, p.err = p.parseNewline(p.left) + } + + if len(p.left) == 0 || p.err != nil { + return false + } + + p.ref, p.left, p.err = p.parseExpression(p.left) + + if p.err != nil { + return false + } + + p.first = false + + if p.ref.Valid() { + return true + } + } +} + +// Expression returns a pointer to the node representing the last successfully +// parsed expression. +func (p *Parser) Expression() *Node { + return p.builder.NodeAt(p.ref) +} + +// Error returns any error that has occurred during parsing. +func (p *Parser) Error() error { + return p.err +} + +// Position describes a position in the input. +type Position struct { + // Number of bytes from the beginning of the input. + Offset int + // Line number, starting at 1. + Line int + // Column number, starting at 1. + Column int +} + +// Shape describes the position of a range in the input. +type Shape struct { + Start Position + End Position +} + +func (p *Parser) position(b []byte) Position { + offset := danger.SubsliceOffset(p.data, b) + + lead := p.data[:offset] + + return Position{ + Offset: offset, + Line: bytes.Count(lead, []byte{'\n'}) + 1, + Column: len(lead) - bytes.LastIndex(lead, []byte{'\n'}), + } +} + +// Shape returns the shape of the given range in the input. Will +// panic if the range is not a subslice of the input. +func (p *Parser) Shape(r Range) Shape { + raw := p.Raw(r) + return Shape{ + Start: p.position(raw), + End: p.position(raw[r.Length:]), + } +} + +func (p *Parser) parseNewline(b []byte) ([]byte, error) { + if b[0] == '\n' { + return b[1:], nil + } + + if b[0] == '\r' { + _, rest, err := scanWindowsNewline(b) + return rest, err + } + + return nil, NewParserError(b[0:1], "expected newline but got %#U", b[0]) +} + +func (p *Parser) parseComment(b []byte) (reference, []byte, error) { + ref := invalidReference + data, rest, err := scanComment(b) + if p.KeepComments && err == nil { + ref = p.builder.Push(Node{ + Kind: Comment, + Raw: p.Range(data), + Data: data, + }) + } + return ref, rest, err +} + +func (p *Parser) parseExpression(b []byte) (reference, []byte, error) { + // expression = ws [ comment ] + // expression =/ ws keyval ws [ comment ] + // expression =/ ws table ws [ comment ] + ref := invalidReference + + b = p.parseWhitespace(b) + + if len(b) == 0 { + return ref, b, nil + } + + if b[0] == '#' { + ref, rest, err := p.parseComment(b) + return ref, rest, err + } + + if b[0] == '\n' || b[0] == '\r' { + return ref, b, nil + } + + var err error + if b[0] == '[' { + ref, b, err = p.parseTable(b) + } else { + ref, b, err = p.parseKeyval(b) + } + + if err != nil { + return ref, nil, err + } + + b = p.parseWhitespace(b) + + if len(b) > 0 && b[0] == '#' { + cref, rest, err := p.parseComment(b) + if cref != invalidReference { + p.builder.Chain(ref, cref) + } + return ref, rest, err + } + + return ref, b, nil +} + +func (p *Parser) parseTable(b []byte) (reference, []byte, error) { + // table = std-table / array-table + if len(b) > 1 && b[1] == '[' { + return p.parseArrayTable(b) + } + + return p.parseStdTable(b) +} + +func (p *Parser) parseArrayTable(b []byte) (reference, []byte, error) { + // array-table = array-table-open key array-table-close + // array-table-open = %x5B.5B ws ; [[ Double left square bracket + // array-table-close = ws %x5D.5D ; ]] Double right square bracket + ref := p.builder.Push(Node{ + Kind: ArrayTable, + }) + + b = b[2:] + b = p.parseWhitespace(b) + + k, b, err := p.parseKey(b) + if err != nil { + return ref, nil, err + } + + p.builder.AttachChild(ref, k) + b = p.parseWhitespace(b) + + b, err = expect(']', b) + if err != nil { + return ref, nil, err + } + + b, err = expect(']', b) + + return ref, b, err +} + +func (p *Parser) parseStdTable(b []byte) (reference, []byte, error) { + // std-table = std-table-open key std-table-close + // std-table-open = %x5B ws ; [ Left square bracket + // std-table-close = ws %x5D ; ] Right square bracket + ref := p.builder.Push(Node{ + Kind: Table, + }) + + b = b[1:] + b = p.parseWhitespace(b) + + key, b, err := p.parseKey(b) + if err != nil { + return ref, nil, err + } + + p.builder.AttachChild(ref, key) + + b = p.parseWhitespace(b) + + b, err = expect(']', b) + + return ref, b, err +} + +func (p *Parser) parseKeyval(b []byte) (reference, []byte, error) { + // keyval = key keyval-sep val + ref := p.builder.Push(Node{ + Kind: KeyValue, + }) + + key, b, err := p.parseKey(b) + if err != nil { + return invalidReference, nil, err + } + + // keyval-sep = ws %x3D ws ; = + + b = p.parseWhitespace(b) + + if len(b) == 0 { + return invalidReference, nil, NewParserError(b, "expected = after a key, but the document ends there") + } + + b, err = expect('=', b) + if err != nil { + return invalidReference, nil, err + } + + b = p.parseWhitespace(b) + + valRef, b, err := p.parseVal(b) + if err != nil { + return ref, b, err + } + + p.builder.Chain(valRef, key) + p.builder.AttachChild(ref, valRef) + + return ref, b, err +} + +//nolint:cyclop,funlen +func (p *Parser) parseVal(b []byte) (reference, []byte, error) { + // val = string / boolean / array / inline-table / date-time / float / integer + ref := invalidReference + + if len(b) == 0 { + return ref, nil, NewParserError(b, "expected value, not eof") + } + + var err error + c := b[0] + + switch c { + case '"': + var raw []byte + var v []byte + if scanFollowsMultilineBasicStringDelimiter(b) { + raw, v, b, err = p.parseMultilineBasicString(b) + } else { + raw, v, b, err = p.parseBasicString(b) + } + + if err == nil { + ref = p.builder.Push(Node{ + Kind: String, + Raw: p.Range(raw), + Data: v, + }) + } + + return ref, b, err + case '\'': + var raw []byte + var v []byte + if scanFollowsMultilineLiteralStringDelimiter(b) { + raw, v, b, err = p.parseMultilineLiteralString(b) + } else { + raw, v, b, err = p.parseLiteralString(b) + } + + if err == nil { + ref = p.builder.Push(Node{ + Kind: String, + Raw: p.Range(raw), + Data: v, + }) + } + + return ref, b, err + case 't': + if !scanFollowsTrue(b) { + return ref, nil, NewParserError(atmost(b, 4), "expected 'true'") + } + + ref = p.builder.Push(Node{ + Kind: Bool, + Data: b[:4], + }) + + return ref, b[4:], nil + case 'f': + if !scanFollowsFalse(b) { + return ref, nil, NewParserError(atmost(b, 5), "expected 'false'") + } + + ref = p.builder.Push(Node{ + Kind: Bool, + Data: b[:5], + }) + + return ref, b[5:], nil + case '[': + return p.parseValArray(b) + case '{': + return p.parseInlineTable(b) + default: + return p.parseIntOrFloatOrDateTime(b) + } +} + +func atmost(b []byte, n int) []byte { + if n >= len(b) { + return b + } + + return b[:n] +} + +func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) { + v, rest, err := scanLiteralString(b) + if err != nil { + return nil, nil, nil, err + } + + return v, v[1 : len(v)-1], rest, nil +} + +func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) { + // inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close + // inline-table-open = %x7B ws ; { + // inline-table-close = ws %x7D ; } + // inline-table-sep = ws %x2C ws ; , Comma + // inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] + parent := p.builder.Push(Node{ + Kind: InlineTable, + Raw: p.Range(b[:1]), + }) + + first := true + + var child reference + + b = b[1:] + + var err error + + for len(b) > 0 { + previousB := b + b = p.parseWhitespace(b) + + if len(b) == 0 { + return parent, nil, NewParserError(previousB[:1], "inline table is incomplete") + } + + if b[0] == '}' { + break + } + + if !first { + b, err = expect(',', b) + if err != nil { + return parent, nil, err + } + b = p.parseWhitespace(b) + } + + var kv reference + + kv, b, err = p.parseKeyval(b) + if err != nil { + return parent, nil, err + } + + if first { + p.builder.AttachChild(parent, kv) + } else { + p.builder.Chain(child, kv) + } + child = kv + + first = false + } + + rest, err := expect('}', b) + + return parent, rest, err +} + +//nolint:funlen,cyclop +func (p *Parser) parseValArray(b []byte) (reference, []byte, error) { + // array = array-open [ array-values ] ws-comment-newline array-close + // array-open = %x5B ; [ + // array-close = %x5D ; ] + // array-values = ws-comment-newline val ws-comment-newline array-sep array-values + // array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] + // array-sep = %x2C ; , Comma + // ws-comment-newline = *( wschar / [ comment ] newline ) + arrayStart := b + b = b[1:] + + parent := p.builder.Push(Node{ + Kind: Array, + }) + + // First indicates whether the parser is looking for the first element + // (non-comment) of the array. + first := true + + lastChild := invalidReference + + addChild := func(valueRef reference) { + if lastChild == invalidReference { + p.builder.AttachChild(parent, valueRef) + } else { + p.builder.Chain(lastChild, valueRef) + } + lastChild = valueRef + } + + var err error + for len(b) > 0 { + cref := invalidReference + cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) + if err != nil { + return parent, nil, err + } + + if cref != invalidReference { + addChild(cref) + } + + if len(b) == 0 { + return parent, nil, NewParserError(arrayStart[:1], "array is incomplete") + } + + if b[0] == ']' { + break + } + + if b[0] == ',' { + if first { + return parent, nil, NewParserError(b[0:1], "array cannot start with comma") + } + b = b[1:] + + cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) + if err != nil { + return parent, nil, err + } + if cref != invalidReference { + addChild(cref) + } + } else if !first { + return parent, nil, NewParserError(b[0:1], "array elements must be separated by commas") + } + + // TOML allows trailing commas in arrays. + if len(b) > 0 && b[0] == ']' { + break + } + + var valueRef reference + valueRef, b, err = p.parseVal(b) + if err != nil { + return parent, nil, err + } + + addChild(valueRef) + + cref, b, err = p.parseOptionalWhitespaceCommentNewline(b) + if err != nil { + return parent, nil, err + } + if cref != invalidReference { + addChild(cref) + } + + first = false + } + + rest, err := expect(']', b) + + return parent, rest, err +} + +func (p *Parser) parseOptionalWhitespaceCommentNewline(b []byte) (reference, []byte, error) { + rootCommentRef := invalidReference + latestCommentRef := invalidReference + + addComment := func(ref reference) { + if rootCommentRef == invalidReference { + rootCommentRef = ref + } else if latestCommentRef == invalidReference { + p.builder.AttachChild(rootCommentRef, ref) + latestCommentRef = ref + } else { + p.builder.Chain(latestCommentRef, ref) + latestCommentRef = ref + } + } + + for len(b) > 0 { + var err error + b = p.parseWhitespace(b) + + if len(b) > 0 && b[0] == '#' { + var ref reference + ref, b, err = p.parseComment(b) + if err != nil { + return invalidReference, nil, err + } + if ref != invalidReference { + addComment(ref) + } + } + + if len(b) == 0 { + break + } + + if b[0] == '\n' || b[0] == '\r' { + b, err = p.parseNewline(b) + if err != nil { + return invalidReference, nil, err + } + } else { + break + } + } + + return rootCommentRef, b, nil +} + +func (p *Parser) parseMultilineLiteralString(b []byte) ([]byte, []byte, []byte, error) { + token, rest, err := scanMultilineLiteralString(b) + if err != nil { + return nil, nil, nil, err + } + + i := 3 + + // skip the immediate new line + if token[i] == '\n' { + i++ + } else if token[i] == '\r' && token[i+1] == '\n' { + i += 2 + } + + return token, token[i : len(token)-3], rest, err +} + +//nolint:funlen,gocognit,cyclop +func (p *Parser) parseMultilineBasicString(b []byte) ([]byte, []byte, []byte, error) { + // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body + // ml-basic-string-delim + // ml-basic-string-delim = 3quotation-mark + // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] + // + // mlb-content = mlb-char / newline / mlb-escaped-nl + // mlb-char = mlb-unescaped / escaped + // mlb-quotes = 1*2quotation-mark + // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii + // mlb-escaped-nl = escape ws newline *( wschar / newline ) + token, escaped, rest, err := scanMultilineBasicString(b) + if err != nil { + return nil, nil, nil, err + } + + i := 3 + + // skip the immediate new line + if token[i] == '\n' { + i++ + } else if token[i] == '\r' && token[i+1] == '\n' { + i += 2 + } + + // fast path + startIdx := i + endIdx := len(token) - len(`"""`) + + if !escaped { + str := token[startIdx:endIdx] + verr := characters.Utf8TomlValidAlreadyEscaped(str) + if verr.Zero() { + return token, str, rest, nil + } + return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") + } + + var builder bytes.Buffer + + // The scanner ensures that the token starts and ends with quotes and that + // escapes are balanced. + for i < len(token)-3 { + c := token[i] + + //nolint:nestif + if c == '\\' { + // When the last non-whitespace character on a line is an unescaped \, + // it will be trimmed along with all whitespace (including newlines) up + // to the next non-whitespace character or closing delimiter. + + isLastNonWhitespaceOnLine := false + j := 1 + findEOLLoop: + for ; j < len(token)-3-i; j++ { + switch token[i+j] { + case ' ', '\t': + continue + case '\r': + if token[i+j+1] == '\n' { + continue + } + case '\n': + isLastNonWhitespaceOnLine = true + } + break findEOLLoop + } + if isLastNonWhitespaceOnLine { + i += j + for ; i < len(token)-3; i++ { + c := token[i] + if !(c == '\n' || c == '\r' || c == ' ' || c == '\t') { + i-- + break + } + } + i++ + continue + } + + // handle escaping + i++ + c = token[i] + + switch c { + case '"', '\\': + builder.WriteByte(c) + case 'b': + builder.WriteByte('\b') + case 'f': + builder.WriteByte('\f') + case 'n': + builder.WriteByte('\n') + case 'r': + builder.WriteByte('\r') + case 't': + builder.WriteByte('\t') + case 'e': + builder.WriteByte(0x1B) + case 'u': + x, err := hexToRune(atmost(token[i+1:], 4), 4) + if err != nil { + return nil, nil, nil, err + } + builder.WriteRune(x) + i += 4 + case 'U': + x, err := hexToRune(atmost(token[i+1:], 8), 8) + if err != nil { + return nil, nil, nil, err + } + + builder.WriteRune(x) + i += 8 + default: + return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) + } + i++ + } else { + size := characters.Utf8ValidNext(token[i:]) + if size == 0 { + return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) + } + builder.Write(token[i : i+size]) + i += size + } + } + + return token, builder.Bytes(), rest, nil +} + +func (p *Parser) parseKey(b []byte) (reference, []byte, error) { + // key = simple-key / dotted-key + // simple-key = quoted-key / unquoted-key + // + // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ + // quoted-key = basic-string / literal-string + // dotted-key = simple-key 1*( dot-sep simple-key ) + // + // dot-sep = ws %x2E ws ; . Period + raw, key, b, err := p.parseSimpleKey(b) + if err != nil { + return invalidReference, nil, err + } + + ref := p.builder.Push(Node{ + Kind: Key, + Raw: p.Range(raw), + Data: key, + }) + + for { + b = p.parseWhitespace(b) + if len(b) > 0 && b[0] == '.' { + b = p.parseWhitespace(b[1:]) + + raw, key, b, err = p.parseSimpleKey(b) + if err != nil { + return ref, nil, err + } + + p.builder.PushAndChain(Node{ + Kind: Key, + Raw: p.Range(raw), + Data: key, + }) + } else { + break + } + } + + return ref, b, nil +} + +func (p *Parser) parseSimpleKey(b []byte) (raw, key, rest []byte, err error) { + if len(b) == 0 { + return nil, nil, nil, NewParserError(b, "expected key but found none") + } + + // simple-key = quoted-key / unquoted-key + // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ + // quoted-key = basic-string / literal-string + switch { + case b[0] == '\'': + return p.parseLiteralString(b) + case b[0] == '"': + return p.parseBasicString(b) + case isUnquotedKeyChar(b[0]): + key, rest = scanUnquotedKey(b) + return key, key, rest, nil + default: + return nil, nil, nil, NewParserError(b[0:1], "invalid character at start of key: %c", b[0]) + } +} + +//nolint:funlen,cyclop +func (p *Parser) parseBasicString(b []byte) ([]byte, []byte, []byte, error) { + // basic-string = quotation-mark *basic-char quotation-mark + // quotation-mark = %x22 ; " + // basic-char = basic-unescaped / escaped + // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii + // escaped = escape escape-seq-char + // escape-seq-char = %x22 ; " quotation mark U+0022 + // escape-seq-char =/ %x5C ; \ reverse solidus U+005C + // escape-seq-char =/ %x62 ; b backspace U+0008 + // escape-seq-char =/ %x66 ; f form feed U+000C + // escape-seq-char =/ %x6E ; n line feed U+000A + // escape-seq-char =/ %x72 ; r carriage return U+000D + // escape-seq-char =/ %x74 ; t tab U+0009 + // escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX + // escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX + token, escaped, rest, err := scanBasicString(b) + if err != nil { + return nil, nil, nil, err + } + + startIdx := len(`"`) + endIdx := len(token) - len(`"`) + + // Fast path. If there is no escape sequence, the string should just be + // an UTF-8 encoded string, which is the same as Go. In that case, + // validate the string and return a direct reference to the buffer. + if !escaped { + str := token[startIdx:endIdx] + verr := characters.Utf8TomlValidAlreadyEscaped(str) + if verr.Zero() { + return token, str, rest, nil + } + return nil, nil, nil, NewParserError(str[verr.Index:verr.Index+verr.Size], "invalid UTF-8") + } + + i := startIdx + + var builder bytes.Buffer + + // The scanner ensures that the token starts and ends with quotes and that + // escapes are balanced. + for i < len(token)-1 { + c := token[i] + if c == '\\' { + i++ + c = token[i] + + switch c { + case '"', '\\': + builder.WriteByte(c) + case 'b': + builder.WriteByte('\b') + case 'f': + builder.WriteByte('\f') + case 'n': + builder.WriteByte('\n') + case 'r': + builder.WriteByte('\r') + case 't': + builder.WriteByte('\t') + case 'e': + builder.WriteByte(0x1B) + case 'u': + x, err := hexToRune(token[i+1:len(token)-1], 4) + if err != nil { + return nil, nil, nil, err + } + + builder.WriteRune(x) + i += 4 + case 'U': + x, err := hexToRune(token[i+1:len(token)-1], 8) + if err != nil { + return nil, nil, nil, err + } + + builder.WriteRune(x) + i += 8 + default: + return nil, nil, nil, NewParserError(token[i:i+1], "invalid escaped character %#U", c) + } + i++ + } else { + size := characters.Utf8ValidNext(token[i:]) + if size == 0 { + return nil, nil, nil, NewParserError(token[i:i+1], "invalid character %#U", c) + } + builder.Write(token[i : i+size]) + i += size + } + } + + return token, builder.Bytes(), rest, nil +} + +func hexToRune(b []byte, length int) (rune, error) { + if len(b) < length { + return -1, NewParserError(b, "unicode point needs %d character, not %d", length, len(b)) + } + b = b[:length] + + var r uint32 + for i, c := range b { + d := uint32(0) + switch { + case '0' <= c && c <= '9': + d = uint32(c - '0') + case 'a' <= c && c <= 'f': + d = uint32(c - 'a' + 10) + case 'A' <= c && c <= 'F': + d = uint32(c - 'A' + 10) + default: + return -1, NewParserError(b[i:i+1], "non-hex character") + } + r = r*16 + d + } + + if r > unicode.MaxRune || 0xD800 <= r && r < 0xE000 { + return -1, NewParserError(b, "escape sequence is invalid Unicode code point") + } + + return rune(r), nil +} + +func (p *Parser) parseWhitespace(b []byte) []byte { + // ws = *wschar + // wschar = %x20 ; Space + // wschar =/ %x09 ; Horizontal tab + _, rest := scanWhitespace(b) + + return rest +} + +//nolint:cyclop +func (p *Parser) parseIntOrFloatOrDateTime(b []byte) (reference, []byte, error) { + switch b[0] { + case 'i': + if !scanFollowsInf(b) { + return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'inf'") + } + + return p.builder.Push(Node{ + Kind: Float, + Data: b[:3], + Raw: p.Range(b[:3]), + }), b[3:], nil + case 'n': + if !scanFollowsNan(b) { + return invalidReference, nil, NewParserError(atmost(b, 3), "expected 'nan'") + } + + return p.builder.Push(Node{ + Kind: Float, + Data: b[:3], + Raw: p.Range(b[:3]), + }), b[3:], nil + case '+', '-': + return p.scanIntOrFloat(b) + } + + if len(b) < 3 { + return p.scanIntOrFloat(b) + } + + s := 5 + if len(b) < s { + s = len(b) + } + + for idx, c := range b[:s] { + if isDigit(c) { + continue + } + + if idx == 2 && c == ':' || (idx == 4 && c == '-') { + return p.scanDateTime(b) + } + + break + } + + return p.scanIntOrFloat(b) +} + +func (p *Parser) scanDateTime(b []byte) (reference, []byte, error) { + // scans for contiguous characters in [0-9T:Z.+-], and up to one space if + // followed by a digit. + hasDate := false + hasTime := false + hasTz := false + seenSpace := false + + i := 0 +byteLoop: + for ; i < len(b); i++ { + c := b[i] + + switch { + case isDigit(c): + case c == '-': + hasDate = true + const minOffsetOfTz = 8 + if i >= minOffsetOfTz { + hasTz = true + } + case c == 'T' || c == 't' || c == ':' || c == '.': + hasTime = true + case c == '+' || c == '-' || c == 'Z' || c == 'z': + hasTz = true + case c == ' ': + if !seenSpace && i+1 < len(b) && isDigit(b[i+1]) { + i += 2 + // Avoid reaching past the end of the document in case the time + // is malformed. See TestIssue585. + if i >= len(b) { + i-- + } + seenSpace = true + hasTime = true + } else { + break byteLoop + } + default: + break byteLoop + } + } + + var kind Kind + + if hasTime { + if hasDate { + if hasTz { + kind = DateTime + } else { + kind = LocalDateTime + } + } else { + kind = LocalTime + } + } else { + kind = LocalDate + } + + return p.builder.Push(Node{ + Kind: kind, + Data: b[:i], + }), b[i:], nil +} + +//nolint:funlen,gocognit,cyclop +func (p *Parser) scanIntOrFloat(b []byte) (reference, []byte, error) { + i := 0 + + if len(b) > 2 && b[0] == '0' && b[1] != '.' && b[1] != 'e' && b[1] != 'E' { + var isValidRune validRuneFn + + switch b[1] { + case 'x': + isValidRune = isValidHexRune + case 'o': + isValidRune = isValidOctalRune + case 'b': + isValidRune = isValidBinaryRune + default: + i++ + } + + if isValidRune != nil { + i += 2 + for ; i < len(b); i++ { + if !isValidRune(b[i]) { + break + } + } + } + + return p.builder.Push(Node{ + Kind: Integer, + Data: b[:i], + Raw: p.Range(b[:i]), + }), b[i:], nil + } + + isFloat := false + + for ; i < len(b); i++ { + c := b[i] + + if c >= '0' && c <= '9' || c == '+' || c == '-' || c == '_' { + continue + } + + if c == '.' || c == 'e' || c == 'E' { + isFloat = true + + continue + } + + if c == 'i' { + if scanFollowsInf(b[i:]) { + return p.builder.Push(Node{ + Kind: Float, + Data: b[:i+3], + Raw: p.Range(b[:i+3]), + }), b[i+3:], nil + } + + return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'i' while scanning for a number") + } + + if c == 'n' { + if scanFollowsNan(b[i:]) { + return p.builder.Push(Node{ + Kind: Float, + Data: b[:i+3], + Raw: p.Range(b[:i+3]), + }), b[i+3:], nil + } + + return invalidReference, nil, NewParserError(b[i:i+1], "unexpected character 'n' while scanning for a number") + } + + break + } + + if i == 0 { + return invalidReference, b, NewParserError(b, "incomplete number") + } + + kind := Integer + + if isFloat { + kind = Float + } + + return p.builder.Push(Node{ + Kind: kind, + Data: b[:i], + Raw: p.Range(b[:i]), + }), b[i:], nil +} + +func isDigit(r byte) bool { + return r >= '0' && r <= '9' +} + +type validRuneFn func(r byte) bool + +func isValidHexRune(r byte) bool { + return r >= 'a' && r <= 'f' || + r >= 'A' && r <= 'F' || + r >= '0' && r <= '9' || + r == '_' +} + +func isValidOctalRune(r byte) bool { + return r >= '0' && r <= '7' || r == '_' +} + +func isValidBinaryRune(r byte) bool { + return r == '0' || r == '1' || r == '_' +} + +func expect(x byte, b []byte) ([]byte, error) { + if len(b) == 0 { + return nil, NewParserError(b, "expected character %c but the document ended here", x) + } + + if b[0] != x { + return nil, NewParserError(b[0:1], "expected character %c", x) + } + + return b[1:], nil +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go new file mode 100644 index 00000000000..0512181d287 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/scanner.go @@ -0,0 +1,270 @@ +package unstable + +import "github.com/pelletier/go-toml/v2/internal/characters" + +func scanFollows(b []byte, pattern string) bool { + n := len(pattern) + + return len(b) >= n && string(b[:n]) == pattern +} + +func scanFollowsMultilineBasicStringDelimiter(b []byte) bool { + return scanFollows(b, `"""`) +} + +func scanFollowsMultilineLiteralStringDelimiter(b []byte) bool { + return scanFollows(b, `'''`) +} + +func scanFollowsTrue(b []byte) bool { + return scanFollows(b, `true`) +} + +func scanFollowsFalse(b []byte) bool { + return scanFollows(b, `false`) +} + +func scanFollowsInf(b []byte) bool { + return scanFollows(b, `inf`) +} + +func scanFollowsNan(b []byte) bool { + return scanFollows(b, `nan`) +} + +func scanUnquotedKey(b []byte) ([]byte, []byte) { + // unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ + for i := 0; i < len(b); i++ { + if !isUnquotedKeyChar(b[i]) { + return b[:i], b[i:] + } + } + + return b, b[len(b):] +} + +func isUnquotedKeyChar(r byte) bool { + return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '-' || r == '_' +} + +func scanLiteralString(b []byte) ([]byte, []byte, error) { + // literal-string = apostrophe *literal-char apostrophe + // apostrophe = %x27 ; ' apostrophe + // literal-char = %x09 / %x20-26 / %x28-7E / non-ascii + for i := 1; i < len(b); { + switch b[i] { + case '\'': + return b[:i+1], b[i+1:], nil + case '\n', '\r': + return nil, nil, NewParserError(b[i:i+1], "literal strings cannot have new lines") + } + size := characters.Utf8ValidNext(b[i:]) + if size == 0 { + return nil, nil, NewParserError(b[i:i+1], "invalid character") + } + i += size + } + + return nil, nil, NewParserError(b[len(b):], "unterminated literal string") +} + +func scanMultilineLiteralString(b []byte) ([]byte, []byte, error) { + // ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body + // ml-literal-string-delim + // ml-literal-string-delim = 3apostrophe + // ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] + // + // mll-content = mll-char / newline + // mll-char = %x09 / %x20-26 / %x28-7E / non-ascii + // mll-quotes = 1*2apostrophe + for i := 3; i < len(b); { + switch b[i] { + case '\'': + if scanFollowsMultilineLiteralStringDelimiter(b[i:]) { + i += 3 + + // At that point we found 3 apostrophe, and i is the + // index of the byte after the third one. The scanner + // needs to be eager, because there can be an extra 2 + // apostrophe that can be accepted at the end of the + // string. + + if i >= len(b) || b[i] != '\'' { + return b[:i], b[i:], nil + } + i++ + + if i >= len(b) || b[i] != '\'' { + return b[:i], b[i:], nil + } + i++ + + if i < len(b) && b[i] == '\'' { + return nil, nil, NewParserError(b[i-3:i+1], "''' not allowed in multiline literal string") + } + + return b[:i], b[i:], nil + } + case '\r': + if len(b) < i+2 { + return nil, nil, NewParserError(b[len(b):], `need a \n after \r`) + } + if b[i+1] != '\n' { + return nil, nil, NewParserError(b[i:i+2], `need a \n after \r`) + } + i += 2 // skip the \n + continue + } + size := characters.Utf8ValidNext(b[i:]) + if size == 0 { + return nil, nil, NewParserError(b[i:i+1], "invalid character") + } + i += size + } + + return nil, nil, NewParserError(b[len(b):], `multiline literal string not terminated by '''`) +} + +func scanWindowsNewline(b []byte) ([]byte, []byte, error) { + const lenCRLF = 2 + if len(b) < lenCRLF { + return nil, nil, NewParserError(b, "windows new line expected") + } + + if b[1] != '\n' { + return nil, nil, NewParserError(b, `windows new line should be \r\n`) + } + + return b[:lenCRLF], b[lenCRLF:], nil +} + +func scanWhitespace(b []byte) ([]byte, []byte) { + for i := 0; i < len(b); i++ { + switch b[i] { + case ' ', '\t': + continue + default: + return b[:i], b[i:] + } + } + + return b, b[len(b):] +} + +func scanComment(b []byte) ([]byte, []byte, error) { + // comment-start-symbol = %x23 ; # + // non-ascii = %x80-D7FF / %xE000-10FFFF + // non-eol = %x09 / %x20-7F / non-ascii + // + // comment = comment-start-symbol *non-eol + + for i := 1; i < len(b); { + if b[i] == '\n' { + return b[:i], b[i:], nil + } + if b[i] == '\r' { + if i+1 < len(b) && b[i+1] == '\n' { + return b[:i+1], b[i+1:], nil + } + return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") + } + size := characters.Utf8ValidNext(b[i:]) + if size == 0 { + return nil, nil, NewParserError(b[i:i+1], "invalid character in comment") + } + + i += size + } + + return b, b[len(b):], nil +} + +func scanBasicString(b []byte) ([]byte, bool, []byte, error) { + // basic-string = quotation-mark *basic-char quotation-mark + // quotation-mark = %x22 ; " + // basic-char = basic-unescaped / escaped + // basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii + // escaped = escape escape-seq-char + escaped := false + i := 1 + + for ; i < len(b); i++ { + switch b[i] { + case '"': + return b[:i+1], escaped, b[i+1:], nil + case '\n', '\r': + return nil, escaped, nil, NewParserError(b[i:i+1], "basic strings cannot have new lines") + case '\\': + if len(b) < i+2 { + return nil, escaped, nil, NewParserError(b[i:i+1], "need a character after \\") + } + escaped = true + i++ // skip the next character + } + } + + return nil, escaped, nil, NewParserError(b[len(b):], `basic string not terminated by "`) +} + +func scanMultilineBasicString(b []byte) ([]byte, bool, []byte, error) { + // ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body + // ml-basic-string-delim + // ml-basic-string-delim = 3quotation-mark + // ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] + // + // mlb-content = mlb-char / newline / mlb-escaped-nl + // mlb-char = mlb-unescaped / escaped + // mlb-quotes = 1*2quotation-mark + // mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii + // mlb-escaped-nl = escape ws newline *( wschar / newline ) + + escaped := false + i := 3 + + for ; i < len(b); i++ { + switch b[i] { + case '"': + if scanFollowsMultilineBasicStringDelimiter(b[i:]) { + i += 3 + + // At that point we found 3 apostrophe, and i is the + // index of the byte after the third one. The scanner + // needs to be eager, because there can be an extra 2 + // apostrophe that can be accepted at the end of the + // string. + + if i >= len(b) || b[i] != '"' { + return b[:i], escaped, b[i:], nil + } + i++ + + if i >= len(b) || b[i] != '"' { + return b[:i], escaped, b[i:], nil + } + i++ + + if i < len(b) && b[i] == '"' { + return nil, escaped, nil, NewParserError(b[i-3:i+1], `""" not allowed in multiline basic string`) + } + + return b[:i], escaped, b[i:], nil + } + case '\\': + if len(b) < i+2 { + return nil, escaped, nil, NewParserError(b[len(b):], "need a character after \\") + } + escaped = true + i++ // skip the next character + case '\r': + if len(b) < i+2 { + return nil, escaped, nil, NewParserError(b[len(b):], `need a \n after \r`) + } + if b[i+1] != '\n' { + return nil, escaped, nil, NewParserError(b[i:i+2], `need a \n after \r`) + } + i++ // skip the \n + } + } + + return nil, escaped, nil, NewParserError(b[len(b):], `multiline basic string not terminated by """`) +} diff --git a/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go new file mode 100644 index 00000000000..00cfd6de458 --- /dev/null +++ b/contrib/tetragon-rthooks/vendor/github.com/pelletier/go-toml/v2/unstable/unmarshaler.go @@ -0,0 +1,7 @@ +package unstable + +// The Unmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a TOML document. +type Unmarshaler interface { + UnmarshalTOML(value *Node) error +} diff --git a/contrib/tetragon-rthooks/vendor/modules.txt b/contrib/tetragon-rthooks/vendor/modules.txt index 017cd48d245..1a73c065948 100644 --- a/contrib/tetragon-rthooks/vendor/modules.txt +++ b/contrib/tetragon-rthooks/vendor/modules.txt @@ -51,7 +51,7 @@ github.com/containers/common/pkg/hooks/1.0.0 # github.com/containers/storage v1.56.0 ## explicit; go 1.22.0 github.com/containers/storage/pkg/fileutils -# github.com/coreos/go-systemd/v22 v22.5.0 +# github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09 ## explicit; go 1.12 github.com/coreos/go-systemd/v22/dbus # github.com/cyphar/filepath-securejoin v0.3.4 @@ -63,7 +63,7 @@ github.com/davecgh/go-spew/spew # github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c ## explicit github.com/docker/go-events -# github.com/godbus/dbus/v5 v5.1.0 +# github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 ## explicit; go 1.12 github.com/godbus/dbus/v5 # github.com/gogo/protobuf v1.3.2 @@ -125,6 +125,11 @@ github.com/opencontainers/runtime-spec/specs-go # github.com/pelletier/go-toml v1.9.5 ## explicit; go 1.12 github.com/pelletier/go-toml +# github.com/pelletier/go-toml/v2 v2.2.3 +## explicit; go 1.21.0 +github.com/pelletier/go-toml/v2/internal/characters +github.com/pelletier/go-toml/v2/internal/danger +github.com/pelletier/go-toml/v2/unstable # github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 ## explicit github.com/pmezard/go-difflib/difflib