Repository: elastic/go-libaudit (go)

Issue:
[rule] Add support for saddr_fam filters
Add support for filtering events based on `saddr_fam`. This is the address family number as found in /usr/include/bits/socket.h. For example, [IPv4 would be 2](https://elixir.bootlin.com/linux/v6.5.7/source/include/linux/socket.h#L192) and [IPv6 would be 10](https://elixir.bootlin.com/linux/v6.5.7/source/include/linux/socket.h#L200).

> -a always,exit -F arch=b64 -S connect,accept,listen,bind  -F a2=16 -F saddr_fam=2 -F success=1 -F key=network-v4
> -a always,exit -F arch=b64 -S connect,accept,listen,bind  -F a2=24 -F saddr_fam=10 -F success=1 -F key=network-v6

https://github.com/linux-audit/audit-kernel/commit/61fc5771f5e729a2ce235af42f69c8506725e84a
https://github.com/linux-audit/audit-userspace/commit/fef497857a57cb9ba3b599731a772162cb39f132


Pull request description:
rule: add saddr_fam rule filter handling
Please take a look.

Closes #144

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d8e762b..e100627 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,8 +7,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 ### Added
 
+- Support `saddr_fam` filters. [#145](https://github.com/elastic/go-libaudit/pull/145)
+
 ### Changed
 
+- Update Vagrant file gvm and ubuntu versions. [#145](https://github.com/elastic/go-libaudit/pull/145)
+
 ### Removed
 
 ### Deprecated
diff --git a/Vagrantfile b/Vagrantfile
index c06b061..51ef046 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -11,7 +11,7 @@ SCRIPT
 install_gvm = <<SCRIPT
 mkdir -p ~/bin
 if [ ! -e "~/bin/gvm" ]; then
-  curl -sL -o ~/bin/gvm https://github.com/andrewkroh/gvm/releases/download/v0.4.0/gvm-linux-amd64
+  curl -sL -o ~/bin/gvm https://github.com/andrewkroh/gvm/releases/download/v0.5.2/gvm-linux-amd64
   chmod +x ~/bin/gvm
   ~/bin/gvm #{GO_VERSION}
   echo 'export GOPATH=$HOME/go' >> ~/.bash_profile
@@ -29,7 +29,7 @@ rm -f golangci-lint-#{GOLANGCI_LINT_VERSION}-linux-amd64.deb
 SCRIPT
 
 Vagrant.configure(2) do |config|
-  config.vm.box = "ubuntu/impish64"
+  config.vm.box = "ubuntu/mantic64"
   config.vm.network :forwarded_port, guest: 22, host: 2228, id: "ssh", auto_correct: true
   config.vm.provision "shell", inline: create_symlink, privileged: false
   config.vm.provision "shell", inline: install_gvm, privileged: false
diff --git a/go.mod b/go.mod
index 3b3bb83..0ff28b7 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/elastic/go-libaudit/v2
 go 1.16
 
 require (
+	github.com/elastic/go-licenser v0.4.1
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
 	github.com/stretchr/testify v1.7.0
 	go.uber.org/multierr v1.7.0
diff --git a/go.sum b/go.sum
index d076ada..ba18c7b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,8 @@
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN48x4=
+github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -9,12 +11,40 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
 go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
diff --git a/rule/defs_kernel_types.go b/rule/defs_kernel_types.go
index e39f14b..c8189a2 100644
--- a/rule/defs_kernel_types.go
+++ b/rule/defs_kernel_types.go
@@ -87,6 +87,7 @@ const (
 	ppidField               field = C.AUDIT_PPID
 	permField               field = C.AUDIT_PERM
 	persField               field = C.AUDIT_PERS
+	saddrFamField           field = C.AUDIT_SADDR_FAM
 	sgidField               field = C.AUDIT_SGID
 	suidField               field = C.AUDIT_SUID
 	subjectClearanceField   field = C.AUDIT_SUBJ_CLR
diff --git a/rule/rule.go b/rule/rule.go
index 33e887e..7c73b97 100644
--- a/rule/rule.go
+++ b/rule/rule.go
@@ -32,6 +32,7 @@ import (
 )
 
 //go:generate sh -c "go tool cgo -godefs defs_kernel_types.go > zkernel_types.go && gofmt -w zkernel_types.go"
+//go:generate go run github.com/elastic/go-licenser
 
 const (
 	maxKeyLength = 256  // AUDIT_MAX_KEY_LEN
@@ -745,6 +746,23 @@ func addFilter(rule *ruleData, lhs, comparator, rhs string) error {
 			return err
 		}
 		rule.values = append(rule.values, inode)
+	case saddrFamField:
+		// Convert RHS to number.
+		num, err := parseNum(rhs)
+		if err != nil {
+			return err
+		}
+		const (
+			// include/linux/socket.h
+			afInet  = 2
+			afInet6 = 10
+		)
+		switch num {
+		case afInet, afInet6:
+		default:
+			return fmt.Errorf("saddr_fam must be 2 or 10: have %d", num)
+		}
+		rule.values = append(rule.values, num)
 	case devMajorField, devMinorField, successField, ppidField:
 		// Flag must be FilterExit.
 		if rule.flags != exitFilter {
diff --git a/rule/tables.go b/rule/tables.go
index 6d121f7..f8ddf97 100644
--- a/rule/tables.go
+++ b/rule/tables.go
@@ -92,6 +92,7 @@ var operatorsTable = map[string]operator{
 	">=": greaterThanOrEqualOperator,
 }
 
+// include/uapi/linux/audit.h
 var fieldsTable = map[string]field{
 	"auid":         auidField,
 	"arch":         archField,
@@ -125,6 +126,7 @@ var fieldsTable = map[string]field{
 	"ppid":         ppidField,
 	"perm":         permField,
 	"pers":         persField,
+	"saddr_fam":    saddrFamField,
 	"sgid":         sgidField,
 	"suid":         suidField,
 	"subj_clr":     subjectClearanceField,
diff --git a/rule/tools.go b/rule/tools.go
new file mode 100644
index 0000000..c57ffad
--- /dev/null
+++ b/rule/tools.go
@@ -0,0 +1,22 @@
+// Licensed to Elasticsearch B.V. under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Elasticsearch B.V. licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//go:build tools
+
+package rule
+
+import _ "github.com/elastic/go-licenser"
diff --git a/rule/zkernel_types.go b/rule/zkernel_types.go
index d993972..a22ab70 100644
--- a/rule/zkernel_types.go
+++ b/rule/zkernel_types.go
@@ -15,7 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
-// Code generated by go tool cgo -godefs - DO NOT EDIT.
+// Code generated by cmd/cgo -godefs; DO NOT EDIT.
 // cgo -godefs defs_kernel_types.go
 
 package rule
@@ -77,6 +77,7 @@ const (
 	ppidField               field = 0x12
 	permField               field = 0x6a
 	persField               field = 0xa
+	saddrFamField           field = 0x71
 	sgidField               field = 0x7
 	suidField               field = 0x3
 	subjectClearanceField   field = 0x11


Test patch:
diff --git a/rule/testdata/71-networking.rules b/rule/testdata/71-networking.rules
index 500759b..237e010 100644
--- a/rule/testdata/71-networking.rules
+++ b/rule/testdata/71-networking.rules
@@ -1,3 +1,5 @@
 ## This is to check if the system is making or recieving connections
 ## externally
 -a always,exit -F arch=b64 -S accept,connect -F key=external-access
+-a always,exit -F arch=b64 -S connect,accept,listen,bind -F a2=16 -F saddr_fam=2 -F success=1 -F key=network-v4
+-a always,exit -F arch=b64 -S connect,accept,listen,bind -F a2=24 -F saddr_fam=10 -F success=1 -F key=network-v6
diff --git a/rule/testdata/71-networking.rules.golden.yml b/rule/testdata/71-networking.rules.golden.yml
index d1e5b19..0280bff 100644
--- a/rule/testdata/71-networking.rules.golden.yml
+++ b/rule/testdata/71-networking.rules.golden.yml
@@ -1,5 +1,5 @@
-# Linux ubuntu-impish 5.13.0-27-generic #29-Ubuntu SMP Wed Jan 12 17:36:47 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
-# auditctl version 3.0
+# Linux ubuntu-mantic 6.5.0-9-generic #9-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct  7 01:35:40 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
+# auditctl version 3.1.1
 rules:
 - flags: -a always,exit -F arch=b64 -S accept,connect -F key=external-access
   bytes: !!binary |
@@ -24,3 +24,51 @@ rules:
     AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
     AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAABleHRlcm5hbC1h
     Y2Nlc3MA
+- flags: -a always,exit -F arch=b64 -S connect,accept,listen,bind -F a2=16 -F saddr_fam=2
+    -F success=1 -F key=network-v4
+  bytes: !!binary |
+    BAAAAAIAAAAFAAAAAAAAAAAMBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAsAAADKAAAAcQAAAGgAAADSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+
+    AADAEAAAAAIAAAABAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAE
+    AAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAABuZXR3b3JrLXY0
+    AAA=
+- flags: -a always,exit -F arch=b64 -S connect,accept,listen,bind -F a2=24 -F saddr_fam=10
+    -F success=1 -F key=network-v6
+  bytes: !!binary |
+    BAAAAAIAAAAFAAAAAAAAAAAMBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAsAAADKAAAAcQAAAGgAAADSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+
+    AADAGAAAAAoAAAABAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAE
+    AAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAABuZXR3b3JrLXY2
+    AAA=