From 4a5776ac7099a0a6eea272cd1ddcb003fdd1f750 Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Fri, 4 Oct 2024 12:01:55 -0400 Subject: [PATCH 1/9] Expose client tools auto update for find endpoint (#46785) * Expose client tools auto update for find endpoint * Group auto update settings in find response Log error instead returning error Add tests auto update settings in find endpoint Add check for not implemented error * Add more test cases --- api/client/webclient/webclient.go | 10 ++++ lib/web/apiserver.go | 22 +++++++- lib/web/apiserver_ping_test.go | 84 +++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index 4acbf407fc381..9a2de71ddcb46 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -296,6 +296,8 @@ type PingResponse struct { ServerVersion string `json:"server_version"` // MinClientVersion is the minimum client version required by the server. MinClientVersion string `json:"min_client_version"` + // AutoUpdateSettings contains the auto update settings. + AutoUpdate AutoUpdateSettings `json:"auto_update"` // ClusterName contains the name of the Teleport cluster. ClusterName string `json:"cluster_name"` @@ -329,6 +331,14 @@ type ProxySettings struct { AssistEnabled bool `json:"assist_enabled"` } +// AutoUpdateSettings contains information about the auto update requirements. +type AutoUpdateSettings struct { + // ToolsVersion defines the version of {tsh, tctl} for client auto update. + ToolsVersion string `json:"tools_version"` + // ToolsAutoUpdate enables client auto update feature. + ToolsAutoUpdate bool `json:"tools_auto_update"` +} + // KubeProxySettings is kubernetes proxy settings type KubeProxySettings struct { // Enabled is true when kubernetes proxy is enabled diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 76a440143b977..19a8e7448c028 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -1524,12 +1524,30 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para if err != nil { return nil, trace.Wrap(err) } - return webclient.PingResponse{ + response := webclient.PingResponse{ Proxy: *proxyConfig, ServerVersion: teleport.Version, MinClientVersion: teleport.MinClientVersion, ClusterName: h.auth.clusterName, - }, nil + } + + autoUpdateConfig, err := h.cfg.AccessPoint.GetAutoUpdateConfig(r.Context()) + // TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions. + if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { + h.logger.ErrorContext(r.Context(), "failed to receive AutoUpdateConfig", "error", err) + } else if err == nil { + response.AutoUpdate.ToolsAutoUpdate = autoUpdateConfig.GetSpec().GetToolsAutoupdate() + } + + autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(r.Context()) + // TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions. + if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { + h.logger.ErrorContext(r.Context(), "failed to receive AutoUpdateVersion", "error", err) + } else if err == nil { + response.AutoUpdate.ToolsVersion = autoUpdateVersion.GetSpec().GetToolsVersion() + } + + return response, nil } func (h *Handler) pingWithConnector(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { diff --git a/lib/web/apiserver_ping_test.go b/lib/web/apiserver_ping_test.go index 8a45e5840e547..9b57e31d80764 100644 --- a/lib/web/apiserver_ping_test.go +++ b/lib/web/apiserver_ping_test.go @@ -26,12 +26,15 @@ import ( "testing" "github.com/gravitational/roundtrip" + "github.com/gravitational/trace" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" + autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/autoupdate" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/modules" ) @@ -236,5 +239,86 @@ func TestPing_minimalAPI(t *testing.T) { require.NoError(t, resp.Body.Close()) }) } +} + +// TestPing_autoUpdateResources tests that find endpoint return correct data related to auto updates. +func TestPing_autoUpdateResources(t *testing.T) { + env := newWebPack(t, 1, func(cfg *proxyConfig) { + cfg.minimalHandler = true + }) + proxy := env.proxies[0] + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + req, err := http.NewRequest(http.MethodGet, proxy.newClient(t).Endpoint("webapi", "find"), nil) + require.NoError(t, err) + req.Host = proxy.handler.handler.cfg.ProxyPublicAddrs[0].Host() + + tests := []struct { + name string + config *autoupdatev1pb.AutoUpdateConfigSpec + version *autoupdatev1pb.AutoUpdateVersionSpec + cleanup bool + expected webclient.AutoUpdateSettings + }{ + { + name: "resources not defined", + expected: webclient.AutoUpdateSettings{}, + }, + { + name: "enable auto update", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, + expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: true}, + cleanup: true, + }, + { + name: "set auto update version", + version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + expected: webclient.AutoUpdateSettings{ToolsVersion: "1.2.3"}, + cleanup: true, + }, + { + name: "enable auto update and set version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, + version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: true, ToolsVersion: "1.2.3"}, + }, + { + name: "modify auto update config and version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: false}, + version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "3.2.1"}, + expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: false, ToolsVersion: "3.2.1"}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.config != nil { + config, err := autoupdate.NewAutoUpdateConfig(tc.config) + require.NoError(t, err) + _, err = env.server.Auth().UpsertAutoUpdateConfig(ctx, config) + require.NoError(t, err) + } + if tc.version != nil { + version, err := autoupdate.NewAutoUpdateVersion(tc.version) + require.NoError(t, err) + _, err = env.server.Auth().UpsertAutoUpdateVersion(ctx, version) + require.NoError(t, err) + } + resp, err := client.NewInsecureWebClient().Do(req) + require.NoError(t, err) + + pr := &webclient.PingResponse{} + require.NoError(t, json.NewDecoder(resp.Body).Decode(pr)) + require.NoError(t, resp.Body.Close()) + + assert.Equal(t, tc.expected, pr.AutoUpdate) + + if tc.cleanup { + require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateConfig(ctx), &trace.NotFoundError{}) + require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateVersion(ctx), &trace.NotFoundError{}) + } + }) + } } From c48243db77ee3a3e6f47304f9edda4b47c84df02 Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Thu, 17 Oct 2024 10:36:06 -0400 Subject: [PATCH 2/9] Client AutoUpdate proto structure changes (#47532) * Update client autoupdate proto structure * Replace with reserved * Fix unit tests * Add more info in proto * Rename proto to be aligned RFD namings * Replace enum type for ToolsMode to string --- api/client/webclient/webclient.go | 4 +- .../teleport/autoupdate/v1/autoupdate.pb.go | 258 ++++++++++++++---- .../teleport/autoupdate/v1/autoupdate.proto | 23 +- api/types/autoupdate/config.go | 12 + api/types/autoupdate/config_test.go | 27 +- api/types/autoupdate/version.go | 12 +- api/types/autoupdate/version_test.go | 20 +- lib/cache/cache_test.go | 8 +- lib/services/local/autoupdate_test.go | 48 +++- lib/web/apiserver.go | 4 +- lib/web/apiserver_ping_test.go | 50 +++- tool/tctl/common/collection.go | 4 +- tool/tctl/common/edit_command_test.go | 32 ++- tool/tctl/common/resource_command_test.go | 6 +- 14 files changed, 389 insertions(+), 119 deletions(-) diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index 9a2de71ddcb46..ba959b20a772f 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -335,8 +335,8 @@ type ProxySettings struct { type AutoUpdateSettings struct { // ToolsVersion defines the version of {tsh, tctl} for client auto update. ToolsVersion string `json:"tools_version"` - // ToolsAutoUpdate enables client auto update feature. - ToolsAutoUpdate bool `json:"tools_auto_update"` + // ToolsMode defines mode client auto update feature `enabled|disabled`. + ToolsMode string `json:"tools_mode"` } // KubeProxySettings is kubernetes proxy settings diff --git a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go index eff70443d2f28..ffc36aeb70163 100644 --- a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go +++ b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go @@ -122,8 +122,7 @@ type AutoUpdateConfigSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // ToolsAutoupdate encodes the feature flag to enable/disable tools autoupdates. - ToolsAutoupdate bool `protobuf:"varint,1,opt,name=tools_autoupdate,json=toolsAutoupdate,proto3" json:"tools_autoupdate,omitempty"` + Tools *AutoUpdateConfigSpecTools `protobuf:"bytes,2,opt,name=tools,proto3" json:"tools,omitempty"` } func (x *AutoUpdateConfigSpec) Reset() { @@ -158,11 +157,60 @@ func (*AutoUpdateConfigSpec) Descriptor() ([]byte, []int) { return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{1} } -func (x *AutoUpdateConfigSpec) GetToolsAutoupdate() bool { +func (x *AutoUpdateConfigSpec) GetTools() *AutoUpdateConfigSpecTools { if x != nil { - return x.ToolsAutoupdate + return x.Tools } - return false + return nil +} + +// AutoUpdateConfigSpecTools encodes the parameters for client tools auto updates. +type AutoUpdateConfigSpecTools struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Mode defines state of the client tools auto update. + Mode string `protobuf:"bytes,1,opt,name=mode,proto3" json:"mode,omitempty"` +} + +func (x *AutoUpdateConfigSpecTools) Reset() { + *x = AutoUpdateConfigSpecTools{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AutoUpdateConfigSpecTools) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AutoUpdateConfigSpecTools) ProtoMessage() {} + +func (x *AutoUpdateConfigSpecTools) ProtoReflect() protoreflect.Message { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AutoUpdateConfigSpecTools.ProtoReflect.Descriptor instead. +func (*AutoUpdateConfigSpecTools) Descriptor() ([]byte, []int) { + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{2} +} + +func (x *AutoUpdateConfigSpecTools) GetMode() string { + if x != nil { + return x.Mode + } + return "" } // AutoUpdateVersion is a resource singleton with version required for @@ -182,7 +230,7 @@ type AutoUpdateVersion struct { func (x *AutoUpdateVersion) Reset() { *x = AutoUpdateVersion{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -195,7 +243,7 @@ func (x *AutoUpdateVersion) String() string { func (*AutoUpdateVersion) ProtoMessage() {} func (x *AutoUpdateVersion) ProtoReflect() protoreflect.Message { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -208,7 +256,7 @@ func (x *AutoUpdateVersion) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoUpdateVersion.ProtoReflect.Descriptor instead. func (*AutoUpdateVersion) Descriptor() ([]byte, []int) { - return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{2} + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{3} } func (x *AutoUpdateVersion) GetKind() string { @@ -252,14 +300,13 @@ type AutoUpdateVersionSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // ToolsVersion is the semantic version required for tools autoupdates. - ToolsVersion string `protobuf:"bytes,1,opt,name=tools_version,json=toolsVersion,proto3" json:"tools_version,omitempty"` + Tools *AutoUpdateVersionSpecTools `protobuf:"bytes,2,opt,name=tools,proto3" json:"tools,omitempty"` } func (x *AutoUpdateVersionSpec) Reset() { *x = AutoUpdateVersionSpec{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -272,7 +319,7 @@ func (x *AutoUpdateVersionSpec) String() string { func (*AutoUpdateVersionSpec) ProtoMessage() {} func (x *AutoUpdateVersionSpec) ProtoReflect() protoreflect.Message { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -285,12 +332,62 @@ func (x *AutoUpdateVersionSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoUpdateVersionSpec.ProtoReflect.Descriptor instead. func (*AutoUpdateVersionSpec) Descriptor() ([]byte, []int) { - return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{3} + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{4} +} + +func (x *AutoUpdateVersionSpec) GetTools() *AutoUpdateVersionSpecTools { + if x != nil { + return x.Tools + } + return nil } -func (x *AutoUpdateVersionSpec) GetToolsVersion() string { +// AutoUpdateVersionSpecTools encodes the parameters for client tools auto updates. +type AutoUpdateVersionSpecTools struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // TargetVersion specifies the semantic version required for tools to establish a connection with the cluster. + // Client tools after connection to the cluster going to be updated to this version automatically. + TargetVersion string `protobuf:"bytes,1,opt,name=target_version,json=targetVersion,proto3" json:"target_version,omitempty"` +} + +func (x *AutoUpdateVersionSpecTools) Reset() { + *x = AutoUpdateVersionSpecTools{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AutoUpdateVersionSpecTools) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AutoUpdateVersionSpecTools) ProtoMessage() {} + +func (x *AutoUpdateVersionSpecTools) ProtoReflect() protoreflect.Message { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AutoUpdateVersionSpecTools.ProtoReflect.Descriptor instead. +func (*AutoUpdateVersionSpecTools) Descriptor() ([]byte, []int) { + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{5} +} + +func (x *AutoUpdateVersionSpecTools) GetTargetVersion() string { if x != nil { - return x.ToolsVersion + return x.TargetVersion } return "" } @@ -317,35 +414,50 @@ var file_teleport_autoupdate_v1_autoupdate_proto_rawDesc = []byte{ 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x41, + 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x77, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, - 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x11, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, - 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, - 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x3c, 0x0a, - 0x15, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, - 0x6f, 0x6f, 0x6c, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x56, 0x5a, 0x54, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, - 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x47, 0x0a, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, + 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x4a, + 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x10, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, 0x61, 0x75, 0x74, + 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x2f, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x6f, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x54, + 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x11, 0x41, 0x75, 0x74, + 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x22, 0x76, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x48, 0x0a, + 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, + 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x0d, 0x74, + 0x6f, 0x6f, 0x6c, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x1a, + 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x53, 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x42, 0x56, 0x5a, 0x54, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x61, + 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -360,24 +472,28 @@ func file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP() []byte { return file_teleport_autoupdate_v1_autoupdate_proto_rawDescData } -var file_teleport_autoupdate_v1_autoupdate_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_teleport_autoupdate_v1_autoupdate_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_teleport_autoupdate_v1_autoupdate_proto_goTypes = []interface{}{ - (*AutoUpdateConfig)(nil), // 0: teleport.autoupdate.v1.AutoUpdateConfig - (*AutoUpdateConfigSpec)(nil), // 1: teleport.autoupdate.v1.AutoUpdateConfigSpec - (*AutoUpdateVersion)(nil), // 2: teleport.autoupdate.v1.AutoUpdateVersion - (*AutoUpdateVersionSpec)(nil), // 3: teleport.autoupdate.v1.AutoUpdateVersionSpec - (*v1.Metadata)(nil), // 4: teleport.header.v1.Metadata + (*AutoUpdateConfig)(nil), // 0: teleport.autoupdate.v1.AutoUpdateConfig + (*AutoUpdateConfigSpec)(nil), // 1: teleport.autoupdate.v1.AutoUpdateConfigSpec + (*AutoUpdateConfigSpecTools)(nil), // 2: teleport.autoupdate.v1.AutoUpdateConfigSpecTools + (*AutoUpdateVersion)(nil), // 3: teleport.autoupdate.v1.AutoUpdateVersion + (*AutoUpdateVersionSpec)(nil), // 4: teleport.autoupdate.v1.AutoUpdateVersionSpec + (*AutoUpdateVersionSpecTools)(nil), // 5: teleport.autoupdate.v1.AutoUpdateVersionSpecTools + (*v1.Metadata)(nil), // 6: teleport.header.v1.Metadata } var file_teleport_autoupdate_v1_autoupdate_proto_depIdxs = []int32{ - 4, // 0: teleport.autoupdate.v1.AutoUpdateConfig.metadata:type_name -> teleport.header.v1.Metadata + 6, // 0: teleport.autoupdate.v1.AutoUpdateConfig.metadata:type_name -> teleport.header.v1.Metadata 1, // 1: teleport.autoupdate.v1.AutoUpdateConfig.spec:type_name -> teleport.autoupdate.v1.AutoUpdateConfigSpec - 4, // 2: teleport.autoupdate.v1.AutoUpdateVersion.metadata:type_name -> teleport.header.v1.Metadata - 3, // 3: teleport.autoupdate.v1.AutoUpdateVersion.spec:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpec - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 2, // 2: teleport.autoupdate.v1.AutoUpdateConfigSpec.tools:type_name -> teleport.autoupdate.v1.AutoUpdateConfigSpecTools + 6, // 3: teleport.autoupdate.v1.AutoUpdateVersion.metadata:type_name -> teleport.header.v1.Metadata + 4, // 4: teleport.autoupdate.v1.AutoUpdateVersion.spec:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpec + 5, // 5: teleport.autoupdate.v1.AutoUpdateVersionSpec.tools:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpecTools + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_teleport_autoupdate_v1_autoupdate_proto_init() } @@ -411,7 +527,7 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { } } file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AutoUpdateVersion); i { + switch v := v.(*AutoUpdateConfigSpecTools); i { case 0: return &v.state case 1: @@ -423,6 +539,18 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { } } file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AutoUpdateVersion); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AutoUpdateVersionSpec); i { case 0: return &v.state @@ -434,6 +562,18 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { return nil } } + file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AutoUpdateVersionSpecTools); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -441,7 +581,7 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_autoupdate_v1_autoupdate_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/api/proto/teleport/autoupdate/v1/autoupdate.proto b/api/proto/teleport/autoupdate/v1/autoupdate.proto index 1987407fe7d81..b4e557549b316 100644 --- a/api/proto/teleport/autoupdate/v1/autoupdate.proto +++ b/api/proto/teleport/autoupdate/v1/autoupdate.proto @@ -33,8 +33,15 @@ message AutoUpdateConfig { // AutoUpdateConfigSpec encodes the parameters of the autoupdate config object. message AutoUpdateConfigSpec { - // ToolsAutoupdate encodes the feature flag to enable/disable tools autoupdates. - bool tools_autoupdate = 1; + reserved 1; + reserved "tools_autoupdate"; // ToolsAutoupdate is replaced by tools.mode. + AutoUpdateConfigSpecTools tools = 2; +} + +// AutoUpdateConfigSpecTools encodes the parameters for client tools auto updates. +message AutoUpdateConfigSpecTools { + // Mode defines state of the client tools auto update. + string mode = 1; } // AutoUpdateVersion is a resource singleton with version required for @@ -50,6 +57,14 @@ message AutoUpdateVersion { // AutoUpdateVersionSpec encodes the parameters of the autoupdate versions. message AutoUpdateVersionSpec { - // ToolsVersion is the semantic version required for tools autoupdates. - string tools_version = 1; + reserved 1; + reserved "tools_version"; // ToolsVersion is replaced by tools.target_version. + AutoUpdateVersionSpecTools tools = 2; +} + +// AutoUpdateVersionSpecTools encodes the parameters for client tools auto updates. +message AutoUpdateVersionSpecTools { + // TargetVersion specifies the semantic version required for tools to establish a connection with the cluster. + // Client tools after connection to the cluster going to be updated to this version automatically. + string target_version = 1; } diff --git a/api/types/autoupdate/config.go b/api/types/autoupdate/config.go index 5be3db89fd0c5..d61c35eccf0c2 100644 --- a/api/types/autoupdate/config.go +++ b/api/types/autoupdate/config.go @@ -26,6 +26,13 @@ import ( "github.com/gravitational/teleport/api/types" ) +const ( + // ToolsUpdateModeEnabled enables client tools automatic updates. + ToolsUpdateModeEnabled = "enabled" + // ToolsUpdateModeDisabled disables client tools automatic updates. + ToolsUpdateModeDisabled = "disabled" +) + // NewAutoUpdateConfig creates a new auto update configuration resource. func NewAutoUpdateConfig(spec *autoupdate.AutoUpdateConfigSpec) (*autoupdate.AutoUpdateConfig, error) { config := &autoupdate.AutoUpdateConfig{ @@ -58,6 +65,11 @@ func ValidateAutoUpdateConfig(c *autoupdate.AutoUpdateConfig) error { if c.Spec == nil { return trace.BadParameter("Spec is nil") } + if c.Spec.Tools != nil { + if c.Spec.Tools.Mode != ToolsUpdateModeDisabled && c.Spec.Tools.Mode != ToolsUpdateModeEnabled { + return trace.BadParameter("ToolsMode is not valid") + } + } return nil } diff --git a/api/types/autoupdate/config_test.go b/api/types/autoupdate/config_test.go index 2ee33dc5bf2b3..443d6f246fa56 100644 --- a/api/types/autoupdate/config_test.go +++ b/api/types/autoupdate/config_test.go @@ -41,7 +41,9 @@ func TestNewAutoUpdateConfig(t *testing.T) { { name: "success tools autoupdate disabled", spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: false, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeDisabled, + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -53,14 +55,18 @@ func TestNewAutoUpdateConfig(t *testing.T) { Name: types.MetaNameAutoUpdateConfig, }, Spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: false, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeDisabled, + }, }, }, }, { name: "success tools autoupdate enabled", spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeEnabled, + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -72,7 +78,9 @@ func TestNewAutoUpdateConfig(t *testing.T) { Name: types.MetaNameAutoUpdateConfig, }, Spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeEnabled, + }, }, }, }, @@ -83,6 +91,17 @@ func TestNewAutoUpdateConfig(t *testing.T) { require.ErrorContains(t, err, "Spec is nil") }, }, + { + name: "invalid tools mode", + spec: &autoupdate.AutoUpdateConfigSpec{ + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: "invalid-mode", + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "ToolsMode is not valid") + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/api/types/autoupdate/version.go b/api/types/autoupdate/version.go index 088171a072ae3..ad2d12f265949 100644 --- a/api/types/autoupdate/version.go +++ b/api/types/autoupdate/version.go @@ -60,11 +60,13 @@ func ValidateAutoUpdateVersion(v *autoupdate.AutoUpdateVersion) error { return trace.BadParameter("Spec is nil") } - if v.Spec.ToolsVersion == "" { - return trace.BadParameter("ToolsVersion is unset") - } - if _, err := semver.NewVersion(v.Spec.ToolsVersion); err != nil { - return trace.BadParameter("ToolsVersion is not a valid semantic version") + if v.Spec.Tools != nil { + if v.Spec.Tools.TargetVersion == "" { + return trace.BadParameter("TargetVersion is unset") + } + if _, err := semver.NewVersion(v.Spec.Tools.TargetVersion); err != nil { + return trace.BadParameter("TargetVersion is not a valid semantic version") + } } return nil diff --git a/api/types/autoupdate/version_test.go b/api/types/autoupdate/version_test.go index 5fe4f167a037e..70790a204b219 100644 --- a/api/types/autoupdate/version_test.go +++ b/api/types/autoupdate/version_test.go @@ -41,7 +41,9 @@ func TestNewAutoUpdateVersion(t *testing.T) { { name: "success tools autoupdate version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3-dev", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3-dev", + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -53,26 +55,32 @@ func TestNewAutoUpdateVersion(t *testing.T) { Name: types.MetaNameAutoUpdateVersion, }, Spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3-dev", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3-dev", + }, }, }, }, { name: "invalid empty tools version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "", + }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "ToolsVersion is unset") + require.ErrorContains(t, err, "TargetVersion is unset") }, }, { name: "invalid semantic tools version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "17-0-0", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "17-0-0", + }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "ToolsVersion is not a valid semantic version") + require.ErrorContains(t, err, "TargetVersion is not a valid semantic version") }, }, { diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index 1e0666be6a340..d058abde83d7d 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -3683,7 +3683,9 @@ func newAutoUpdateConfig(t *testing.T) *autoupdate.AutoUpdateConfig { t.Helper() r, err := update.NewAutoUpdateConfig(&autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: update.ToolsUpdateModeEnabled, + }, }) require.NoError(t, err) return r @@ -3693,7 +3695,9 @@ func newAutoUpdateVersion(t *testing.T) *autoupdate.AutoUpdateVersion { t.Helper() r, err := update.NewAutoUpdateVersion(&autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, }) require.NoError(t, err) return r diff --git a/lib/services/local/autoupdate_test.go b/lib/services/local/autoupdate_test.go index 77e13937ac47a..ae858d65cc47c 100644 --- a/lib/services/local/autoupdate_test.go +++ b/lib/services/local/autoupdate_test.go @@ -51,7 +51,11 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) { Kind: types.KindAutoUpdateConfig, Version: types.V1, Metadata: &headerv1.Metadata{Name: types.MetaNameAutoUpdateConfig}, - Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, + Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, } created, err := service.CreateAutoUpdateConfig(ctx, config) @@ -72,10 +76,12 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) { require.Empty(t, diff) require.Equal(t, created.GetMetadata().GetRevision(), got.GetMetadata().GetRevision()) - config.Spec.ToolsAutoupdate = false + config.Spec.Tools = &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeDisabled, + } updated, err := service.UpdateAutoUpdateConfig(ctx, config) require.NoError(t, err) - require.NotEqual(t, got.GetSpec().GetToolsAutoupdate(), updated.GetSpec().GetToolsAutoupdate()) + require.NotEqual(t, got.GetSpec().GetTools(), updated.GetSpec().GetTools()) _, err = service.UpsertAutoUpdateConfig(ctx, config) require.NoError(t, err) @@ -107,7 +113,11 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) { Kind: types.KindAutoUpdateVersion, Version: types.V1, Metadata: &headerv1.Metadata{Name: types.MetaNameAutoUpdateVersion}, - Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, } created, err := service.CreateAutoUpdateVersion(ctx, version) @@ -128,10 +138,12 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) { require.Empty(t, diff) require.Equal(t, created.GetMetadata().GetRevision(), got.GetMetadata().GetRevision()) - version.Spec.ToolsVersion = "3.2.1" + version.Spec.Tools = &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "3.2.1", + } updated, err := service.UpdateAutoUpdateVersion(ctx, version) require.NoError(t, err) - require.NotEqual(t, got.GetSpec().GetToolsVersion(), updated.GetSpec().GetToolsVersion()) + require.NotEqual(t, got.GetSpec().GetTools().GetTargetVersion(), updated.GetSpec().GetTools().GetTargetVersion()) _, err = service.UpsertAutoUpdateVersion(ctx, version) require.NoError(t, err) @@ -163,7 +175,11 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) { Kind: types.KindAutoUpdateConfig, Version: types.V1, Metadata: &headerv1.Metadata{Name: "invalid-auto-update-config-name"}, - Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, + Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, } createdConfig, err := service.CreateAutoUpdateConfig(ctx, config) @@ -174,7 +190,11 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) { Kind: types.KindAutoUpdateVersion, Version: types.V1, Metadata: &headerv1.Metadata{Name: "invalid-auto-update-version-name"}, - Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, } createdVersion, err := service.CreateAutoUpdateVersion(ctx, version) @@ -196,7 +216,11 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) { ctx := context.Background() // Validate the config update restriction. - config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}) + config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }) require.NoError(t, err) createdConfig, err := service.UpsertAutoUpdateConfig(ctx, config) @@ -209,7 +233,11 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) { require.Nil(t, createdConfig) // Validate the version update restriction. - version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}) + version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }) require.NoError(t, err) createdVersion, err := service.UpsertAutoUpdateVersion(ctx, version) diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 19a8e7448c028..dca0512e948c1 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -1536,7 +1536,7 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { h.logger.ErrorContext(r.Context(), "failed to receive AutoUpdateConfig", "error", err) } else if err == nil { - response.AutoUpdate.ToolsAutoUpdate = autoUpdateConfig.GetSpec().GetToolsAutoupdate() + response.AutoUpdate.ToolsMode = autoUpdateConfig.GetSpec().GetTools().GetMode() } autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(r.Context()) @@ -1544,7 +1544,7 @@ func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Para if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { h.logger.ErrorContext(r.Context(), "failed to receive AutoUpdateVersion", "error", err) } else if err == nil { - response.AutoUpdate.ToolsVersion = autoUpdateVersion.GetSpec().GetToolsVersion() + response.AutoUpdate.ToolsVersion = autoUpdateVersion.GetSpec().GetTools().GetTargetVersion() } return response, nil diff --git a/lib/web/apiserver_ping_test.go b/lib/web/apiserver_ping_test.go index 9b57e31d80764..202355f517134 100644 --- a/lib/web/apiserver_ping_test.go +++ b/lib/web/apiserver_ping_test.go @@ -267,28 +267,52 @@ func TestPing_autoUpdateResources(t *testing.T) { expected: webclient.AutoUpdateSettings{}, }, { - name: "enable auto update", - config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, - expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: true}, + name: "enable auto update", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, + expected: webclient.AutoUpdateSettings{ToolsMode: "enabled"}, cleanup: true, }, { - name: "set auto update version", - version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + name: "set auto update version", + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, expected: webclient.AutoUpdateSettings{ToolsVersion: "1.2.3"}, cleanup: true, }, { - name: "enable auto update and set version", - config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, - version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, - expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: true, ToolsVersion: "1.2.3"}, + name: "enable auto update and set version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, + expected: webclient.AutoUpdateSettings{ToolsMode: "enabled", ToolsVersion: "1.2.3"}, }, { - name: "modify auto update config and version", - config: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: false}, - version: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "3.2.1"}, - expected: webclient.AutoUpdateSettings{ToolsAutoUpdate: false, ToolsVersion: "3.2.1"}, + name: "modify auto update config and version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeDisabled, + }, + }, + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "3.2.1", + }, + }, + expected: webclient.AutoUpdateSettings{ToolsMode: "disabled", ToolsVersion: "3.2.1"}, }, } for _, tc := range tests { diff --git a/tool/tctl/common/collection.go b/tool/tctl/common/collection.go index 5910c21188a64..0b70145323d89 100644 --- a/tool/tctl/common/collection.go +++ b/tool/tctl/common/collection.go @@ -1692,7 +1692,7 @@ func (c *autoUpdateConfigCollection) writeText(w io.Writer, verbose bool) error t := asciitable.MakeTable([]string{"Name", "Tools AutoUpdate Enabled"}) t.AddRow([]string{ c.config.GetMetadata().GetName(), - fmt.Sprintf("%v", c.config.GetSpec().GetToolsAutoupdate()), + fmt.Sprintf("%v", c.config.GetSpec().GetTools().GetMode()), }) _, err := t.AsBuffer().WriteTo(w) return trace.Wrap(err) @@ -1710,7 +1710,7 @@ func (c *autoUpdateVersionCollection) writeText(w io.Writer, verbose bool) error t := asciitable.MakeTable([]string{"Name", "Tools AutoUpdate Version"}) t.AddRow([]string{ c.version.GetMetadata().GetName(), - fmt.Sprintf("%v", c.version.GetSpec().GetToolsVersion()), + fmt.Sprintf("%v", c.version.GetSpec().GetTools().TargetVersion), }) _, err := t.AsBuffer().WriteTo(w) return trace.Wrap(err) diff --git a/tool/tctl/common/edit_command_test.go b/tool/tctl/common/edit_command_test.go index 4eac9de223b9f..cdb6f7ab1fd1a 100644 --- a/tool/tctl/common/edit_command_test.go +++ b/tool/tctl/common/edit_command_test.go @@ -497,10 +497,18 @@ func testEditSAMLConnector(t *testing.T, clt *authclient.Client) { func testEditAutoUpdateConfig(t *testing.T, clt *authclient.Client) { ctx := context.Background() - expected, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}) + expected, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }) require.NoError(t, err) - initial, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: false}) + initial, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeDisabled, + }, + }) require.NoError(t, err) serviceClient := autoupdatev1pb.NewAutoUpdateServiceClient(clt.GetConnection()) @@ -523,18 +531,26 @@ func testEditAutoUpdateConfig(t *testing.T, clt *authclient.Client) { actual, err := clt.GetAutoUpdateConfig(ctx) require.NoError(t, err, "failed to get autoupdate config after edit") - assert.NotEqual(t, initial.GetSpec().GetToolsAutoupdate(), actual.GetSpec().GetToolsAutoupdate(), + assert.NotEqual(t, initial.GetSpec().GetTools().Mode, actual.GetSpec().GetTools().GetMode(), "tools_autoupdate should have been modified by edit") - assert.Equal(t, expected.GetSpec().GetToolsAutoupdate(), actual.GetSpec().GetToolsAutoupdate()) + assert.Equal(t, expected.GetSpec().GetTools().GetMode(), actual.GetSpec().GetTools().GetMode()) } func testEditAutoUpdateVersion(t *testing.T, clt *authclient.Client) { ctx := context.Background() - expected, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "3.2.1"}) + expected, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "3.2.1", + }, + }) require.NoError(t, err) - initial, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}) + initial, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }) require.NoError(t, err) serviceClient := autoupdatev1pb.NewAutoUpdateServiceClient(clt.GetConnection()) @@ -557,7 +573,7 @@ func testEditAutoUpdateVersion(t *testing.T, clt *authclient.Client) { actual, err := clt.GetAutoUpdateVersion(ctx) require.NoError(t, err, "failed to get autoupdate version after edit") - assert.NotEqual(t, initial.GetSpec().GetToolsVersion(), actual.GetSpec().GetToolsVersion(), + assert.NotEqual(t, initial.GetSpec().GetTools().GetTargetVersion(), actual.GetSpec().GetTools().GetTargetVersion(), "tools_autoupdate should have been modified by edit") - assert.Equal(t, expected.GetSpec().GetToolsVersion(), actual.GetSpec().GetToolsVersion()) + assert.Equal(t, expected.GetSpec().GetTools().GetTargetVersion(), actual.GetSpec().GetTools().GetTargetVersion()) } diff --git a/tool/tctl/common/resource_command_test.go b/tool/tctl/common/resource_command_test.go index be15e22a8f9b6..603d303a7aa4a 100644 --- a/tool/tctl/common/resource_command_test.go +++ b/tool/tctl/common/resource_command_test.go @@ -2199,7 +2199,8 @@ metadata: name: autoupdate-config revision: 3a43b44a-201e-4d7f-aef1-ae2f6d9811ed spec: - tools_autoupdate: true + tools: + mode: enabled version: v1 ` _, err := runResourceCommand(t, clt, []string{"get", types.KindAutoUpdateConfig, "--format=json"}) @@ -2234,7 +2235,8 @@ metadata: name: autoupdate-version revision: 3a43b44a-201e-4d7f-aef1-ae2f6d9811ed spec: - tools_version: 1.2.3 + tools: + target_version: 1.2.3 version: v1 ` _, err := runResourceCommand(t, clt, []string{"get", types.KindAutoUpdateVersion, "--format=json"}) From 36ae92bb97e34ee7ce6312eeefee5f855477c6aa Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Wed, 9 Oct 2024 19:26:32 -0400 Subject: [PATCH 3/9] Add packaging utility for client tools auto updates (#47060) * Add packaging utility for client tools auto updates * Add error handling for close functions * Move archive to existing utils package * Move archive helpers to integration/helper CR changes * CR changes * CR changes * CR changes Replace creating directory with extract path as argument * CR changes * Validate full size before un-archive Extract files to extractDir with ignore dir structure * Change compressing with relative paths Add test for cleanup and fix skip logic * CR changes * CR changes * Fix linter --- integration/helpers/archive.go | 170 +++++++++++++++++++ lib/utils/disk.go | 17 ++ lib/utils/disk_windows.go | 18 +- lib/utils/packaging/unarchive.go | 158 +++++++++++++++++ lib/utils/packaging/unarchive_test.go | 147 ++++++++++++++++ lib/utils/packaging/unarchive_unix.go | 205 +++++++++++++++++++++++ lib/utils/packaging/unarchive_windows.go | 30 ++++ 7 files changed, 744 insertions(+), 1 deletion(-) create mode 100644 integration/helpers/archive.go create mode 100644 lib/utils/packaging/unarchive.go create mode 100644 lib/utils/packaging/unarchive_test.go create mode 100644 lib/utils/packaging/unarchive_unix.go create mode 100644 lib/utils/packaging/unarchive_windows.go diff --git a/integration/helpers/archive.go b/integration/helpers/archive.go new file mode 100644 index 0000000000000..6e48108013d86 --- /dev/null +++ b/integration/helpers/archive.go @@ -0,0 +1,170 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package helpers + +import ( + "archive/tar" + "archive/zip" + "compress/gzip" + "context" + "io" + "log/slog" + "os" + "os/exec" + "path/filepath" + "runtime" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport" +) + +// CompressDirToZipFile compresses a source directory into `.zip` format and stores at `archivePath`, +// preserving the relative file path structure of the source directory. +func CompressDirToZipFile(ctx context.Context, sourceDir, archivePath string) (err error) { + archive, err := os.Create(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer func() { + if closeErr := archive.Close(); closeErr != nil { + err = trace.NewAggregate(err, closeErr) + return + } + if err != nil { + if err := os.Remove(archivePath); err != nil { + slog.ErrorContext(ctx, "failed to remove archive", "error", err) + } + } + }() + + zipWriter := zip.NewWriter(archive) + err = filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return trace.Wrap(err) + } + if info.IsDir() { + return nil + } + file, err := os.Open(path) + if err != nil { + return trace.Wrap(err) + } + defer file.Close() + relPath, err := filepath.Rel(sourceDir, path) + if err != nil { + return trace.Wrap(err) + } + zipFileWriter, err := zipWriter.Create(relPath) + if err != nil { + return trace.Wrap(err) + } + if _, err = io.Copy(zipFileWriter, file); err != nil { + return trace.Wrap(err) + } + return trace.Wrap(file.Close()) + }) + if err != nil { + return trace.Wrap(err) + } + if err = zipWriter.Close(); err != nil { + return trace.Wrap(err) + } + + return +} + +// CompressDirToTarGzFile compresses a source directory into .tar.gz format and stores at `archivePath`, +// preserving the relative file path structure of the source directory. +func CompressDirToTarGzFile(ctx context.Context, sourceDir, archivePath string) (err error) { + archive, err := os.Create(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer func() { + if closeErr := archive.Close(); closeErr != nil { + err = trace.NewAggregate(err, closeErr) + return + } + if err != nil { + if err := os.Remove(archivePath); err != nil { + slog.ErrorContext(ctx, "failed to remove archive", "error", err) + } + } + }() + gzipWriter := gzip.NewWriter(archive) + tarWriter := tar.NewWriter(gzipWriter) + err = filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + header, err := tar.FileInfoHeader(info, info.Name()) + if err != nil { + return trace.Wrap(err) + } + header.Name, err = filepath.Rel(sourceDir, path) + if err != nil { + return trace.Wrap(err) + } + if err := tarWriter.WriteHeader(header); err != nil { + return trace.Wrap(err) + } + if _, err = io.Copy(tarWriter, file); err != nil { + return trace.Wrap(err) + } + return trace.Wrap(file.Close()) + }) + if err != nil { + return trace.Wrap(err) + } + if err = tarWriter.Close(); err != nil { + return trace.Wrap(err) + } + if err = gzipWriter.Close(); err != nil { + return trace.Wrap(err) + } + + return +} + +// CompressDirToPkgFile runs for the macOS `pkgbuild` command to generate a .pkg +// archive file from the source directory. +func CompressDirToPkgFile(ctx context.Context, sourceDir, archivePath, identifier string) error { + if runtime.GOOS != "darwin" { + return trace.BadParameter("only darwin platform is supported for pkg file") + } + cmd := exec.CommandContext( + ctx, + "pkgbuild", + "--root", sourceDir, + "--identifier", identifier, + "--version", teleport.Version, + archivePath, + ) + + return trace.Wrap(cmd.Run()) +} diff --git a/lib/utils/disk.go b/lib/utils/disk.go index 78fba5457099b..9e2419527051a 100644 --- a/lib/utils/disk.go +++ b/lib/utils/disk.go @@ -46,6 +46,23 @@ func PercentUsed(path string) (float64, error) { return Round(ratio * 100), nil } +// FreeDiskWithReserve returns the available disk space (in bytes) on the disk at dir, minus `reservedFreeDisk`. +func FreeDiskWithReserve(dir string, reservedFreeDisk uint64) (uint64, error) { + var stat syscall.Statfs_t + err := syscall.Statfs(dir, &stat) + if err != nil { + return 0, trace.Wrap(err) + } + if stat.Bsize < 0 { + return 0, trace.Errorf("invalid size") + } + avail := stat.Bavail * uint64(stat.Bsize) + if reservedFreeDisk > avail { + return 0, trace.Errorf("no free space left") + } + return avail - reservedFreeDisk, nil +} + // CanUserWriteTo attempts to check if a user has write access to certain path. // It also works around the program being run as root and tries to check // the permissions of the user who executed the program as root. diff --git a/lib/utils/disk_windows.go b/lib/utils/disk_windows.go index cde568b4c9589..8056849afa82b 100644 --- a/lib/utils/disk_windows.go +++ b/lib/utils/disk_windows.go @@ -21,13 +21,29 @@ package utils -import "github.com/gravitational/trace" +import ( + "github.com/gravitational/trace" + "golang.org/x/sys/windows" +) // PercentUsed is not supported on Windows. func PercentUsed(path string) (float64, error) { return 0.0, trace.NotImplemented("disk usage not supported on Windows") } +// FreeDiskWithReserve returns the available disk space (in bytes) on the disk at dir, minus `reservedFreeDisk`. +func FreeDiskWithReserve(dir string, reservedFreeDisk uint64) (uint64, error) { + var avail uint64 + err := windows.GetDiskFreeSpaceEx(windows.StringToUTF16Ptr(dir), &avail, nil, nil) + if err != nil { + return 0, trace.Wrap(err) + } + if reservedFreeDisk > avail { + return 0, trace.Errorf("no free space left") + } + return avail - reservedFreeDisk, nil +} + // CanUserWriteTo is not supported on Windows. func CanUserWriteTo(path string) (bool, error) { return false, trace.NotImplemented("path permission checking is not supported on Windows") diff --git a/lib/utils/packaging/unarchive.go b/lib/utils/packaging/unarchive.go new file mode 100644 index 0000000000000..f1a197e095b1a --- /dev/null +++ b/lib/utils/packaging/unarchive.go @@ -0,0 +1,158 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package packaging + +import ( + "archive/zip" + "io" + "os" + "path/filepath" + "slices" + "strings" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/lib/utils" +) + +const ( + // reservedFreeDisk is the predefined amount of free disk space (in bytes) required + // to remain available after extracting Teleport binaries. + reservedFreeDisk = 10 * 1024 * 1024 +) + +// RemoveWithSuffix removes all that matches the provided suffix, except for file or directory with `skipName`. +func RemoveWithSuffix(dir, suffix, skipName string) error { + var removePaths []string + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return trace.Wrap(err) + } + if skipName == info.Name() { + return nil + } + if !strings.HasSuffix(info.Name(), suffix) { + return nil + } + removePaths = append(removePaths, path) + if info.IsDir() { + return filepath.SkipDir + } + return nil + }) + if err != nil { + return trace.Wrap(err) + } + for _, path := range removePaths { + if err := os.RemoveAll(path); err != nil { + return trace.Wrap(err) + } + } + return nil +} + +// replaceZip un-archives the Teleport package in .zip format, iterates through +// the compressed content, and ignores everything not matching the binaries specified +// in the execNames argument. The data is extracted to extractDir, and symlinks are created +// in toolsDir pointing to the extractDir path with binaries. +func replaceZip(toolsDir string, archivePath string, extractDir string, execNames []string) error { + f, err := os.Open(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return trace.Wrap(err) + } + zipReader, err := zip.NewReader(f, fi.Size()) + if err != nil { + return trace.Wrap(err) + } + + var totalSize uint64 = 0 + for _, zipFile := range zipReader.File { + baseName := filepath.Base(zipFile.Name) + // Skip over any files in the archive that are not defined execNames. + if !slices.ContainsFunc(execNames, func(s string) bool { + return baseName == s + }) { + continue + } + totalSize += zipFile.UncompressedSize64 + } + // Verify that we have enough space for uncompressed zipFile. + if err := checkFreeSpace(extractDir, totalSize); err != nil { + return trace.Wrap(err) + } + + for _, zipFile := range zipReader.File { + baseName := filepath.Base(zipFile.Name) + // Skip over any files in the archive that are not defined execNames. + if !slices.Contains(execNames, baseName) { + continue + } + + if err := func(zipFile *zip.File) error { + file, err := zipFile.Open() + if err != nil { + return trace.Wrap(err) + } + defer file.Close() + + dest := filepath.Join(extractDir, baseName) + destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + return trace.Wrap(err) + } + defer destFile.Close() + + if _, err := io.Copy(destFile, file); err != nil { + return trace.Wrap(err) + } + appPath := filepath.Join(toolsDir, baseName) + if err := os.Remove(appPath); err != nil && !os.IsNotExist(err) { + return trace.Wrap(err) + } + if err := os.Symlink(dest, appPath); err != nil { + return trace.Wrap(err) + } + return trace.Wrap(destFile.Close()) + }(zipFile); err != nil { + return trace.Wrap(err) + } + } + + return nil +} + +// checkFreeSpace verifies that we have enough requested space (in bytes) at specific directory. +func checkFreeSpace(path string, requested uint64) error { + free, err := utils.FreeDiskWithReserve(path, reservedFreeDisk) + if err != nil { + return trace.Errorf("failed to calculate free disk in %q: %v", path, err) + } + // Bail if there's not enough free disk space at the target. + if requested > free { + return trace.Errorf("%q needs %d additional bytes of disk space", path, requested-free) + } + + return nil +} diff --git a/lib/utils/packaging/unarchive_test.go b/lib/utils/packaging/unarchive_test.go new file mode 100644 index 0000000000000..30933bbb75927 --- /dev/null +++ b/lib/utils/packaging/unarchive_test.go @@ -0,0 +1,147 @@ +//go:build !windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package packaging + +import ( + "context" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/integration/helpers" +) + +// TestPackaging verifies un-archiving of all supported teleport package formats. +func TestPackaging(t *testing.T) { + script := "#!/bin/sh\necho test" + + sourceDir, err := os.MkdirTemp(os.TempDir(), "source") + require.NoError(t, err) + + toolsDir, err := os.MkdirTemp(os.TempDir(), "dest") + require.NoError(t, err) + + extractDir, err := os.MkdirTemp(toolsDir, "extract") + require.NoError(t, err) + + t.Cleanup(func() { + require.NoError(t, os.RemoveAll(extractDir)) + require.NoError(t, os.RemoveAll(sourceDir)) + require.NoError(t, os.RemoveAll(toolsDir)) + }) + + // Create test script for packaging in relative path `teleport\bin` to ensure that + // binaries going to be identified and extracted flatten to `extractDir`. + binPath := filepath.Join(sourceDir, "teleport", "bin") + require.NoError(t, os.MkdirAll(binPath, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(binPath, "tsh"), []byte(script), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(binPath, "tctl"), []byte(script), 0o755)) + + ctx := context.Background() + + t.Run("tar.gz", func(t *testing.T) { + archivePath := filepath.Join(toolsDir, "tsh.tar.gz") + err = helpers.CompressDirToTarGzFile(ctx, sourceDir, archivePath) + require.NoError(t, err) + require.FileExists(t, archivePath, "archive not created") + + // For the .tar.gz format we extract app by app to check that content discard is not required. + err = replaceTarGz(toolsDir, archivePath, extractDir, []string{"tctl"}) + require.NoError(t, err) + err = replaceTarGz(toolsDir, archivePath, extractDir, []string{"tsh"}) + require.NoError(t, err) + assert.FileExists(t, filepath.Join(toolsDir, "tsh"), "script not created") + assert.FileExists(t, filepath.Join(toolsDir, "tctl"), "script not created") + + data, err := os.ReadFile(filepath.Join(toolsDir, "tsh")) + require.NoError(t, err) + assert.Equal(t, script, string(data)) + }) + + t.Run("pkg", func(t *testing.T) { + if runtime.GOOS != "darwin" { + t.Skip("unsupported platform") + } + archivePath := filepath.Join(toolsDir, "tsh.pkg") + err = helpers.CompressDirToPkgFile(ctx, sourceDir, archivePath, "com.example.pkgtest") + require.NoError(t, err) + require.FileExists(t, archivePath, "archive not created") + + err = replacePkg(toolsDir, archivePath, filepath.Join(extractDir, "apps"), []string{"tsh", "tctl"}) + require.NoError(t, err) + assert.FileExists(t, filepath.Join(toolsDir, "tsh"), "script not created") + assert.FileExists(t, filepath.Join(toolsDir, "tctl"), "script not created") + + data, err := os.ReadFile(filepath.Join(toolsDir, "tsh")) + require.NoError(t, err) + assert.Equal(t, script, string(data)) + }) + + t.Run("zip", func(t *testing.T) { + archivePath := filepath.Join(toolsDir, "tsh.zip") + err = helpers.CompressDirToZipFile(ctx, sourceDir, archivePath) + require.NoError(t, err) + require.FileExists(t, archivePath, "archive not created") + + err = replaceZip(toolsDir, archivePath, extractDir, []string{"tsh", "tctl"}) + require.NoError(t, err) + assert.FileExists(t, filepath.Join(toolsDir, "tsh"), "script not created") + assert.FileExists(t, filepath.Join(toolsDir, "tctl"), "script not created") + + data, err := os.ReadFile(filepath.Join(toolsDir, "tsh")) + require.NoError(t, err) + assert.Equal(t, script, string(data)) + }) +} + +// TestRemoveWithSuffix verifies that helper for the cleanup removes directories +func TestRemoveWithSuffix(t *testing.T) { + testDir := t.TempDir() + dirForRemove := "test-extract-pkg" + + // Creates directories `test/test-extract-pkg/test-extract-pkg` with exact names + // to ensure that only root one going to be removed recursively without any error. + path := filepath.Join(testDir, dirForRemove, dirForRemove) + require.NoError(t, os.MkdirAll(path, 0o755)) + // Also we create the directory that needs to be skipped, and it matches the remove + // pattern `test/skip-test-extract-pkg/test-extract-pkg`. + skipName := "skip-" + dirForRemove + skipPath := filepath.Join(testDir, skipName) + dirInSkipPath := filepath.Join(skipPath, dirForRemove) + require.NoError(t, os.MkdirAll(skipPath, 0o755)) + + err := RemoveWithSuffix(testDir, dirForRemove, skipName) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(testDir, dirForRemove)) + assert.True(t, os.IsNotExist(err)) + + filePath, err := os.Stat(skipPath) + require.NoError(t, err) + assert.True(t, filePath.IsDir()) + + _, err = os.Stat(dirInSkipPath) + assert.True(t, os.IsNotExist(err)) +} diff --git a/lib/utils/packaging/unarchive_unix.go b/lib/utils/packaging/unarchive_unix.go new file mode 100644 index 0000000000000..3be7d0c473ef9 --- /dev/null +++ b/lib/utils/packaging/unarchive_unix.go @@ -0,0 +1,205 @@ +//go:build !windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package packaging + +import ( + "archive/tar" + "compress/gzip" + "errors" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "slices" + + "github.com/google/renameio/v2" + "github.com/gravitational/trace" +) + +// ReplaceToolsBinaries extracts executables specified by execNames from archivePath into +// extractDir. After each executable is extracted, it is symlinked from extractDir/[name] to +// toolsDir/[name]. +// +// For Darwin, archivePath must be a .pkg file. +// For other POSIX, archivePath must be a gzipped tarball. +func ReplaceToolsBinaries(toolsDir string, archivePath string, extractDir string, execNames []string) error { + switch runtime.GOOS { + case "darwin": + return replacePkg(toolsDir, archivePath, extractDir, execNames) + default: + return replaceTarGz(toolsDir, archivePath, extractDir, execNames) + } +} + +// replaceTarGz un-archives the Teleport package in .tar.gz format, iterates through +// the compressed content, and ignores everything not matching the app binaries specified +// in the apps argument. The data is extracted to extractDir, and symlinks are created +// in toolsDir pointing to the extractDir path with binaries. +func replaceTarGz(toolsDir string, archivePath string, extractDir string, execNames []string) error { + if err := validateFreeSpaceTarGz(archivePath, extractDir, execNames); err != nil { + return trace.Wrap(err) + } + f, err := os.Open(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer f.Close() + + gzipReader, err := gzip.NewReader(f) + if err != nil { + return trace.Wrap(err) + } + tarReader := tar.NewReader(gzipReader) + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return trace.Wrap(err) + } + baseName := filepath.Base(header.Name) + // Skip over any files in the archive that are not in execNames. + if !slices.Contains(execNames, baseName) { + continue + } + + if err = func(header *tar.Header) error { + tempFile, err := renameio.TempFile(extractDir, filepath.Join(toolsDir, baseName)) + if err != nil { + return trace.Wrap(err) + } + defer tempFile.Cleanup() + if err := os.Chmod(tempFile.Name(), 0o755); err != nil { + return trace.Wrap(err) + } + if _, err := io.Copy(tempFile, tarReader); err != nil { + return trace.Wrap(err) + } + if err := tempFile.CloseAtomicallyReplace(); err != nil { + return trace.Wrap(err) + } + return trace.Wrap(tempFile.Cleanup()) + }(header); err != nil { + return trace.Wrap(err) + } + } + + return trace.Wrap(gzipReader.Close()) +} + +// validateFreeSpaceTarGz validates that extraction size match available disk space in `extractDir`. +func validateFreeSpaceTarGz(archivePath string, extractDir string, execNames []string) error { + f, err := os.Open(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer f.Close() + + var totalSize uint64 + gzipReader, err := gzip.NewReader(f) + if err != nil { + return trace.Wrap(err) + } + tarReader := tar.NewReader(gzipReader) + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return trace.Wrap(err) + } + baseName := filepath.Base(header.Name) + // Skip over any files in the archive that are not defined execNames. + if !slices.Contains(execNames, baseName) { + continue + } + totalSize += uint64(header.Size) + } + + return trace.Wrap(checkFreeSpace(extractDir, totalSize)) +} + +// replacePkg expands the Teleport package in .pkg format using the platform-dependent pkgutil utility. +// The data is extracted to extractDir, and symlinks are created in toolsDir pointing to the binaries +// in extractDir. Before creating the symlinks, each binary must be executed at least once to pass +// OS signature verification. +func replacePkg(toolsDir string, archivePath string, extractDir string, execNames []string) error { + // Use "pkgutil" from the filesystem to expand the archive. In theory .pkg + // files are xz archives, however it's still safer to use "pkgutil" in-case + // Apple makes non-standard changes to the format. + // + // Full command: pkgutil --expand-full NAME.pkg DIRECTORY/ + pkgutil, err := exec.LookPath("pkgutil") + if err != nil { + return trace.Wrap(err) + } + + if err = exec.Command(pkgutil, "--expand-full", archivePath, extractDir).Run(); err != nil { + return trace.Wrap(err) + } + + err = filepath.Walk(extractDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return trace.Wrap(err) + } + if info.IsDir() { + return nil + } + // Skip over any files in the archive that are not in execNames. + if !slices.ContainsFunc(execNames, func(s string) bool { + return filepath.Base(info.Name()) == s + }) { + return nil + } + + // The first time a signed and notarized binary macOS application is run, + // execution is paused while it gets sent to Apple to verify. Once Apple + // approves the binary, the "com.apple.macl" extended attribute is added + // and the process is allowed to execute. This process is not concurrent, any + // other operations (like moving the application) on the application during + // this time will lead to the application being sent SIGKILL. + // + // Since apps have to be concurrent, execute app before performing any + // swap operations. This ensures that the "com.apple.macl" extended + // attribute is set and macOS will not send a SIGKILL to the process + // if multiple processes are trying to operate on it. + command := exec.Command(path, "version", "--client") + if err := command.Run(); err != nil { + return trace.Wrap(err) + } + + // Due to macOS applications not being a single binary (they are a + // directory), atomic operations are not possible. To work around this, use + // a symlink (which can be atomically swapped), then do a cleanup pass + // removing any stale copies of the expanded package. + newName := filepath.Join(toolsDir, filepath.Base(path)) + if err := renameio.Symlink(path, newName); err != nil { + return trace.Wrap(err) + } + + return nil + }) + + return trace.Wrap(err) +} diff --git a/lib/utils/packaging/unarchive_windows.go b/lib/utils/packaging/unarchive_windows.go new file mode 100644 index 0000000000000..c07471adce83c --- /dev/null +++ b/lib/utils/packaging/unarchive_windows.go @@ -0,0 +1,30 @@ +//go:build windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package packaging + +// ReplaceToolsBinaries extracts executables specified by execNames from archivePath into +// extractDir. After each executable is extracted, it is symlinked from extractDir/[name] to +// toolsDir/[name]. +// +// For Windows, archivePath must be a .zip file. +func ReplaceToolsBinaries(toolsDir string, archivePath string, extractPath string, execNames []string) error { + return replaceZip(toolsDir, archivePath, extractPath, execNames) +} From a81e9b7e66c48c5bd86dae089c9235b1955af327 Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Fri, 18 Oct 2024 16:34:56 -0400 Subject: [PATCH 4/9] Client tools auto update (#47466) * Add client tools auto update * Replace fork for posix platform for re-exec Move integration tests to client tools specific dir Use context cancellation with SIGTERM, SIGINT Remove cancelable tee reader with context replacement Renaming * Fix syscall path execution Fix archive cleanup if hash is not valid Limit the archive write bytes * Cover the case with single package for darwin platform after v17 * Move updater logic to tools package * Move context out from the library Base URL renaming * Add more context in comments * Changes in find endpoint * Replace test http server with `httptest` Replace hash for bytes matching Proper temp file close for archive download * Add more context to comments * Move feature flag to main package to be reused * Constant rename * Replace build tag with lib/modules to identify enterprise build * Replace fips tag with modules flag --- integration/autoupdate/tools/helper_test.go | 89 ++++ .../autoupdate/tools/helper_unix_test.go | 37 ++ .../autoupdate/tools/helper_windows_test.go | 42 ++ integration/autoupdate/tools/main_test.go | 173 ++++++++ integration/autoupdate/tools/updater/main.go | 88 ++++ integration/autoupdate/tools/updater_test.go | 231 ++++++++++ lib/autoupdate/tools/progress.go | 43 ++ lib/autoupdate/tools/updater.go | 400 ++++++++++++++++++ lib/autoupdate/tools/utils.go | 175 ++++++++ lib/utils/disk.go | 6 +- lib/utils/packaging/unarchive_unix.go | 4 +- 11 files changed, 1283 insertions(+), 5 deletions(-) create mode 100644 integration/autoupdate/tools/helper_test.go create mode 100644 integration/autoupdate/tools/helper_unix_test.go create mode 100644 integration/autoupdate/tools/helper_windows_test.go create mode 100644 integration/autoupdate/tools/main_test.go create mode 100644 integration/autoupdate/tools/updater/main.go create mode 100644 integration/autoupdate/tools/updater_test.go create mode 100644 lib/autoupdate/tools/progress.go create mode 100644 lib/autoupdate/tools/updater.go create mode 100644 lib/autoupdate/tools/utils.go diff --git a/integration/autoupdate/tools/helper_test.go b/integration/autoupdate/tools/helper_test.go new file mode 100644 index 0000000000000..a3c37a9e94b55 --- /dev/null +++ b/integration/autoupdate/tools/helper_test.go @@ -0,0 +1,89 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "net/http" + "sync" +) + +type limitRequest struct { + limit int64 + lock chan struct{} +} + +// limitedResponseWriter wraps http.ResponseWriter and enforces a write limit +// then block the response until signal is received. +type limitedResponseWriter struct { + requests chan limitRequest +} + +// newLimitedResponseWriter creates a new limitedResponseWriter with the lock. +func newLimitedResponseWriter() *limitedResponseWriter { + lw := &limitedResponseWriter{ + requests: make(chan limitRequest, 10), + } + return lw +} + +// Wrap wraps response writer if limit was previously requested, if not, return original one. +func (lw *limitedResponseWriter) Wrap(w http.ResponseWriter) http.ResponseWriter { + select { + case request := <-lw.requests: + return &wrapper{ + ResponseWriter: w, + request: request, + } + default: + return w + } +} + +// SetLimitRequest sends limit request to the pool to wrap next response writer with defined limits. +func (lw *limitedResponseWriter) SetLimitRequest(limit limitRequest) { + lw.requests <- limit +} + +// wrapper wraps the http response writer to control writing operation by blocking it. +type wrapper struct { + http.ResponseWriter + + written int64 + request limitRequest + released bool + + mutex sync.Mutex +} + +// Write writes data to the underlying ResponseWriter but respects the byte limit. +func (lw *wrapper) Write(p []byte) (int, error) { + lw.mutex.Lock() + defer lw.mutex.Unlock() + + if lw.written >= lw.request.limit && !lw.released { + // Send signal that lock is acquired and wait till it was released by response. + lw.request.lock <- struct{}{} + <-lw.request.lock + lw.released = true + } + + n, err := lw.ResponseWriter.Write(p) + lw.written += int64(n) + return n, err +} diff --git a/integration/autoupdate/tools/helper_unix_test.go b/integration/autoupdate/tools/helper_unix_test.go new file mode 100644 index 0000000000000..61ba0766b90d4 --- /dev/null +++ b/integration/autoupdate/tools/helper_unix_test.go @@ -0,0 +1,37 @@ +//go:build !windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "errors" + "syscall" + + "github.com/gravitational/trace" +) + +// sendInterrupt sends a SIGINT to the process. +func sendInterrupt(pid int) error { + err := syscall.Kill(pid, syscall.SIGINT) + if errors.Is(err, syscall.ESRCH) { + return trace.BadParameter("can't find the process: %v", pid) + } + return trace.Wrap(err) +} diff --git a/integration/autoupdate/tools/helper_windows_test.go b/integration/autoupdate/tools/helper_windows_test.go new file mode 100644 index 0000000000000..b2ede9ade8c19 --- /dev/null +++ b/integration/autoupdate/tools/helper_windows_test.go @@ -0,0 +1,42 @@ +//go:build windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "syscall" + + "github.com/gravitational/trace" + "golang.org/x/sys/windows" +) + +var ( + kernel = windows.NewLazyDLL("kernel32.dll") + ctrlEvent = kernel.NewProc("GenerateConsoleCtrlEvent") +) + +// sendInterrupt sends a Ctrl-Break event to the process. +func sendInterrupt(pid int) error { + r, _, err := ctrlEvent.Call(uintptr(syscall.CTRL_BREAK_EVENT), uintptr(pid)) + if r == 0 { + return trace.Wrap(err) + } + return nil +} diff --git a/integration/autoupdate/tools/main_test.go b/integration/autoupdate/tools/main_test.go new file mode 100644 index 0000000000000..a14a6dc9fc683 --- /dev/null +++ b/integration/autoupdate/tools/main_test.go @@ -0,0 +1,173 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "log" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/integration/helpers" +) + +const ( + testBinaryName = "updater" + teleportToolsVersion = "TELEPORT_TOOLS_VERSION" +) + +var ( + // testVersions list of the pre-compiled binaries with encoded versions to check. + testVersions = []string{ + "1.2.3", + "3.2.1", + } + limitedWriter = newLimitedResponseWriter() + + toolsDir string + baseURL string +) + +func TestMain(m *testing.M) { + ctx := context.Background() + tmp, err := os.MkdirTemp(os.TempDir(), testBinaryName) + if err != nil { + log.Fatalf("failed to create temporary directory: %v", err) + } + + toolsDir, err = os.MkdirTemp(os.TempDir(), "tools") + if err != nil { + log.Fatalf("failed to create temporary directory: %v", err) + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + filePath := filepath.Join(tmp, r.URL.Path) + switch { + case strings.HasSuffix(r.URL.Path, ".sha256"): + serve256File(w, r, strings.TrimSuffix(filePath, ".sha256")) + default: + http.ServeFile(limitedWriter.Wrap(w), r, filePath) + } + })) + baseURL = server.URL + for _, version := range testVersions { + if err := buildAndArchiveApps(ctx, tmp, toolsDir, version, server.URL); err != nil { + log.Fatalf("failed to build testing app binary archive: %v", err) + } + } + + // Run tests after binary is built. + code := m.Run() + + server.Close() + if err := os.RemoveAll(tmp); err != nil { + log.Fatalf("failed to remove temporary directory: %v", err) + } + if err := os.RemoveAll(toolsDir); err != nil { + log.Fatalf("failed to remove tools directory: %v", err) + } + + os.Exit(code) +} + +// serve256File calculates sha256 checksum for requested file. +func serve256File(w http.ResponseWriter, _ *http.Request, filePath string) { + log.Printf("Calculating and serving file checksum: %s\n", filePath) + + w.Header().Set("Content-Disposition", "attachment; filename=\""+filepath.Base(filePath)+".sha256\"") + w.Header().Set("Content-Type", "plain/text") + + file, err := os.Open(filePath) + if errors.Is(err, os.ErrNotExist) { + http.Error(w, "file not found", http.StatusNotFound) + return + } + if err != nil { + http.Error(w, "failed to open file", http.StatusInternalServerError) + return + } + defer file.Close() + + hash := sha256.New() + if _, err := io.Copy(hash, file); err != nil { + http.Error(w, "failed to write to hash", http.StatusInternalServerError) + return + } + if _, err := hex.NewEncoder(w).Write(hash.Sum(nil)); err != nil { + http.Error(w, "failed to write checksum", http.StatusInternalServerError) + } +} + +// buildAndArchiveApps compiles the updater integration and pack it depends on platform is used. +func buildAndArchiveApps(ctx context.Context, path string, toolsDir string, version string, baseURL string) error { + versionPath := filepath.Join(path, version) + for _, app := range []string{"tsh", "tctl"} { + output := filepath.Join(versionPath, app) + switch runtime.GOOS { + case "windows": + output = filepath.Join(versionPath, app+".exe") + case "darwin": + output = filepath.Join(versionPath, app+".app", "Contents", "MacOS", app) + } + if err := buildBinary(output, toolsDir, version, baseURL); err != nil { + return trace.Wrap(err) + } + } + switch runtime.GOOS { + case "darwin": + archivePath := filepath.Join(path, fmt.Sprintf("teleport-%s.pkg", version)) + return trace.Wrap(helpers.CompressDirToPkgFile(ctx, versionPath, archivePath, "com.example.pkgtest")) + case "windows": + archivePath := filepath.Join(path, fmt.Sprintf("teleport-v%s-windows-amd64-bin.zip", version)) + return trace.Wrap(helpers.CompressDirToZipFile(ctx, versionPath, archivePath)) + default: + archivePath := filepath.Join(path, fmt.Sprintf("teleport-v%s-linux-%s-bin.tar.gz", version, runtime.GOARCH)) + return trace.Wrap(helpers.CompressDirToTarGzFile(ctx, versionPath, archivePath)) + } +} + +// buildBinary executes command to build binary with updater logic only for testing. +func buildBinary(output string, toolsDir string, version string, baseURL string) error { + cmd := exec.Command( + "go", "build", "-o", output, + "-ldflags", strings.Join([]string{ + fmt.Sprintf("-X 'main.toolsDir=%s'", toolsDir), + fmt.Sprintf("-X 'main.version=%s'", version), + fmt.Sprintf("-X 'main.baseURL=%s'", baseURL), + }, " "), + "./updater", + ) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return trace.Wrap(cmd.Run()) +} diff --git a/integration/autoupdate/tools/updater/main.go b/integration/autoupdate/tools/updater/main.go new file mode 100644 index 0000000000000..e14c76e5d5aa8 --- /dev/null +++ b/integration/autoupdate/tools/updater/main.go @@ -0,0 +1,88 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package main + +import ( + "context" + "errors" + "fmt" + "log" + "os" + "os/signal" + "runtime" + "syscall" + "time" + + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/lib/autoupdate/tools" +) + +var ( + version = "development" + baseURL = "http://localhost" + toolsDir = "" +) + +func main() { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + ctx, _ = signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) + + updater := tools.NewUpdater( + clientTools(), + toolsDir, + version, + tools.WithBaseURL(baseURL), + ) + toolsVersion, reExec := updater.CheckLocal() + if reExec { + // Download and update the version of client tools required by the cluster. + // This is required if the user passed in the TELEPORT_TOOLS_VERSION explicitly. + err := updater.UpdateWithLock(ctx, toolsVersion) + if errors.Is(err, context.Canceled) { + os.Exit(0) + return + } + if err != nil { + log.Fatalf("failed to download version (%v): %v\n", toolsVersion, err) + return + } + + // Re-execute client tools with the correct version of client tools. + code, err := updater.Exec() + if err != nil { + log.Fatalf("Failed to re-exec client tool: %v\n", err) + } else { + os.Exit(code) + } + } + if len(os.Args) > 1 && os.Args[1] == "version" { + fmt.Printf("Teleport v%v git\n", version) + } +} + +// clientTools list of the client tools needs to be updated. +func clientTools() []string { + switch runtime.GOOS { + case constants.WindowsOS: + return []string{"tsh.exe", "tctl.exe"} + default: + return []string{"tsh", "tctl"} + } +} diff --git a/integration/autoupdate/tools/updater_test.go b/integration/autoupdate/tools/updater_test.go new file mode 100644 index 0000000000000..96d5486462067 --- /dev/null +++ b/integration/autoupdate/tools/updater_test.go @@ -0,0 +1,231 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/lib/autoupdate/tools" +) + +var ( + // pattern is template for response on version command for client tools {tsh, tctl}. + pattern = regexp.MustCompile(`(?m)Teleport v(.*) git`) +) + +// TestUpdate verifies the basic update logic. We first download a lower version, then request +// an update to a newer version, expecting it to re-execute with the updated version. +func TestUpdate(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Fetch compiled test binary with updater logic and install to $TELEPORT_HOME. + updater := tools.NewUpdater( + clientTools(), + toolsDir, + testVersions[0], + tools.WithBaseURL(baseURL), + ) + err := updater.Update(ctx, testVersions[0]) + require.NoError(t, err) + + // Verify that the installed version is equal to requested one. + cmd := exec.CommandContext(ctx, filepath.Join(toolsDir, "tsh"), "version") + out, err := cmd.Output() + require.NoError(t, err) + + matches := pattern.FindStringSubmatch(string(out)) + require.Len(t, matches, 2) + require.Equal(t, testVersions[0], matches[1]) + + // Execute version command again with setting the new version which must + // trigger re-execution of the same command after downloading requested version. + cmd = exec.CommandContext(ctx, filepath.Join(toolsDir, "tsh"), "version") + cmd.Env = append( + os.Environ(), + fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]), + ) + out, err = cmd.Output() + require.NoError(t, err) + + matches = pattern.FindStringSubmatch(string(out)) + require.Len(t, matches, 2) + require.Equal(t, testVersions[1], matches[1]) +} + +// TestParallelUpdate launches multiple updater commands in parallel while defining a new version. +// The first process should acquire a lock and block execution for the other processes. After the +// first update is complete, other processes should acquire the lock one by one and re-execute +// the command with the updated version without any new downloads. +func TestParallelUpdate(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Initial fetch the updater binary un-archive and replace. + updater := tools.NewUpdater( + clientTools(), + toolsDir, + testVersions[0], + tools.WithBaseURL(baseURL), + ) + err := updater.Update(ctx, testVersions[0]) + require.NoError(t, err) + + // By setting the limit request next test http serving file going blocked until unlock is sent. + lock := make(chan struct{}) + limitedWriter.SetLimitRequest(limitRequest{ + limit: 1024, + lock: lock, + }) + + outputs := make([]bytes.Buffer, 3) + errChan := make(chan error, 3) + for i := 0; i < len(outputs); i++ { + cmd := exec.Command(filepath.Join(toolsDir, "tsh"), "version") + cmd.Stdout = &outputs[i] + cmd.Stderr = &outputs[i] + cmd.Env = append( + os.Environ(), + fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]), + ) + err = cmd.Start() + require.NoError(t, err, "failed to start updater") + + go func(cmd *exec.Cmd) { + errChan <- cmd.Wait() + }(cmd) + } + + select { + case err := <-errChan: + require.Fail(t, "we shouldn't receive any error", err) + case <-time.After(5 * time.Second): + require.Fail(t, "failed to wait till the download is started") + case <-lock: + // Wait for a short period to allow other processes to launch and attempt to acquire the lock. + time.Sleep(100 * time.Millisecond) + lock <- struct{}{} + } + + // Wait till process finished with exit code 0, but we still should get progress + // bar in output content. + for i := 0; i < cap(outputs); i++ { + select { + case <-time.After(5 * time.Second): + require.Fail(t, "failed to wait till the process is finished") + case err := <-errChan: + require.NoError(t, err) + } + } + + var progressCount int + for i := 0; i < cap(outputs); i++ { + matches := pattern.FindStringSubmatch(outputs[i].String()) + require.Len(t, matches, 2) + assert.Equal(t, testVersions[1], matches[1]) + if strings.Contains(outputs[i].String(), "Update progress:") { + progressCount++ + } + } + assert.Equal(t, 1, progressCount, "we should have only one progress bar downloading new version") +} + +// TestUpdateInterruptSignal verifies the interrupt signal send to the process must stop downloading. +func TestUpdateInterruptSignal(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Initial fetch the updater binary un-archive and replace. + updater := tools.NewUpdater( + clientTools(), + toolsDir, + testVersions[0], + tools.WithBaseURL(baseURL), + ) + err := updater.Update(ctx, testVersions[0]) + require.NoError(t, err) + + var output bytes.Buffer + cmd := exec.Command(filepath.Join(toolsDir, "tsh"), "version") + cmd.Stdout = &output + cmd.Stderr = &output + cmd.Env = append( + os.Environ(), + fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]), + ) + err = cmd.Start() + require.NoError(t, err, "failed to start updater") + pid := cmd.Process.Pid + + errChan := make(chan error) + go func() { + errChan <- cmd.Wait() + }() + + // By setting the limit request next test http serving file going blocked until unlock is sent. + lock := make(chan struct{}) + limitedWriter.SetLimitRequest(limitRequest{ + limit: 1024, + lock: lock, + }) + + select { + case err := <-errChan: + require.Fail(t, "we shouldn't receive any error", err) + case <-time.After(5 * time.Second): + require.Fail(t, "failed to wait till the download is started") + case <-lock: + time.Sleep(100 * time.Millisecond) + require.NoError(t, sendInterrupt(pid)) + lock <- struct{}{} + } + + // Wait till process finished with exit code 0, but we still should get progress + // bar in output content. + select { + case <-time.After(5 * time.Second): + require.Fail(t, "failed to wait till the process interrupted") + case err := <-errChan: + require.NoError(t, err) + } + assert.Contains(t, output.String(), "Update progress:") +} + +func clientTools() []string { + switch runtime.GOOS { + case constants.WindowsOS: + return []string{"tsh.exe", "tctl.exe"} + default: + return []string{"tsh", "tctl"} + } +} diff --git a/lib/autoupdate/tools/progress.go b/lib/autoupdate/tools/progress.go new file mode 100644 index 0000000000000..95395003730ec --- /dev/null +++ b/lib/autoupdate/tools/progress.go @@ -0,0 +1,43 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools + +import ( + "fmt" + "strings" +) + +type progressWriter struct { + n int64 + limit int64 +} + +func (w *progressWriter) Write(p []byte) (int, error) { + w.n = w.n + int64(len(p)) + + n := int((w.n*100)/w.limit) / 10 + bricks := strings.Repeat("â–’", n) + strings.Repeat(" ", 10-n) + fmt.Print("\rUpdate progress: [" + bricks + "] (Ctrl-C to cancel update)") + + if w.n == w.limit { + fmt.Print("\n") + } + + return len(p), nil +} diff --git a/lib/autoupdate/tools/updater.go b/lib/autoupdate/tools/updater.go new file mode 100644 index 0000000000000..96991044ccc31 --- /dev/null +++ b/lib/autoupdate/tools/updater.go @@ -0,0 +1,400 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools + +import ( + "bytes" + "context" + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "fmt" + "io" + "log/slog" + "net/http" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "syscall" + "time" + + "github.com/google/uuid" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/client/webclient" + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/api/types/autoupdate" + "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/utils/packaging" +) + +const ( + // teleportToolsVersionEnv is environment name for requesting specific version for update. + teleportToolsVersionEnv = "TELEPORT_TOOLS_VERSION" + // baseURL is CDN URL for downloading official Teleport packages. + baseURL = "https://cdn.teleport.dev" + // reservedFreeDisk is the predefined amount of free disk space (in bytes) required + // to remain available after downloading archives. + reservedFreeDisk = 10 * 1024 * 1024 // 10 Mb + // lockFileName is file used for locking update process in parallel. + lockFileName = ".lock" + // updatePackageSuffix is directory suffix used for package extraction in tools directory. + updatePackageSuffix = "-update-pkg" +) + +var ( + // // pattern is template for response on version command for client tools {tsh, tctl}. + pattern = regexp.MustCompile(`(?m)Teleport v(.*) git`) +) + +// Option applies an option value for the Updater. +type Option func(u *Updater) + +// WithBaseURL defines custom base url for the updater. +func WithBaseURL(baseURL string) Option { + return func(u *Updater) { + u.baseURL = baseURL + } +} + +// WithClient defines custom http client for the Updater. +func WithClient(client *http.Client) Option { + return func(u *Updater) { + u.client = client + } +} + +// Updater is updater implementation for the client tools auto updates. +type Updater struct { + toolsDir string + localVersion string + tools []string + + baseURL string + client *http.Client +} + +// NewUpdater initializes the updater for client tools auto updates. We need to specify the list +// of tools (e.g., `tsh`, `tctl`) that should be updated, the tools directory path where we +// download, extract package archives with the new version, and replace symlinks (e.g., `$TELEPORT_HOME/bin`). +// The base URL of the CDN with Teleport packages and the `http.Client` can be customized via options. +func NewUpdater(tools []string, toolsDir string, localVersion string, options ...Option) *Updater { + updater := &Updater{ + tools: tools, + toolsDir: toolsDir, + localVersion: localVersion, + baseURL: baseURL, + client: http.DefaultClient, + } + for _, option := range options { + option(updater) + } + + return updater +} + +// CheckLocal is run at client tool startup and will only perform local checks. +// Returns the version needs to be updated and re-executed, by re-execution flag we +// understand that update and re-execute is required. +func (u *Updater) CheckLocal() (version string, reExec bool) { + // Check if the user has requested a specific version of client tools. + requestedVersion := os.Getenv(teleportToolsVersionEnv) + switch requestedVersion { + // The user has turned off any form of automatic updates. + case "off": + return "", false + // Requested version already the same as client version. + case u.localVersion: + return u.localVersion, false + } + + // If a version of client tools has already been downloaded to + // tools directory, return that. + toolsVersion, err := checkToolVersion(u.toolsDir) + if err != nil { + return "", false + } + // The user has requested a specific version of client tools. + if requestedVersion != "" && requestedVersion != toolsVersion { + return requestedVersion, true + } + + return toolsVersion, false +} + +// CheckRemote first checks the version set by the environment variable. If not set or disabled, +// it checks against the Proxy Service to determine if client tools need updating by requesting +// the `webapi/find` handler, which stores information about the required client tools version to +// operate with this cluster. It returns the semantic version that needs updating and whether +// re-execution is necessary, by re-execution flag we understand that update and re-execute is required. +func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string) (version string, reExec bool, err error) { + // Check if the user has requested a specific version of client tools. + requestedVersion := os.Getenv(teleportToolsVersionEnv) + switch requestedVersion { + // The user has turned off any form of automatic updates. + case "off": + return "", false, nil + // Requested version already the same as client version. + case u.localVersion: + return u.localVersion, false, nil + } + + certPool, err := x509.SystemCertPool() + if err != nil { + return "", false, trace.Wrap(err) + } + resp, err := webclient.Find(&webclient.Config{ + Context: ctx, + ProxyAddr: proxyAddr, + Pool: certPool, + Timeout: 30 * time.Second, + }) + if err != nil { + return "", false, trace.Wrap(err) + } + + // If a version of client tools has already been downloaded to + // tools directory, return that. + toolsVersion, err := checkToolVersion(u.toolsDir) + if err != nil { + return "", false, trace.Wrap(err) + } + + switch { + case requestedVersion != "" && requestedVersion != toolsVersion: + return requestedVersion, true, nil + case resp.AutoUpdate.ToolsMode != autoupdate.ToolsUpdateModeEnabled || resp.AutoUpdate.ToolsVersion == "": + return "", false, nil + case u.localVersion == resp.AutoUpdate.ToolsVersion: + return resp.AutoUpdate.ToolsVersion, false, nil + case resp.AutoUpdate.ToolsVersion != toolsVersion: + return resp.AutoUpdate.ToolsVersion, true, nil + } + + return toolsVersion, false, nil +} + +// UpdateWithLock acquires filesystem lock, downloads requested version package, +// unarchive and replace existing one. +func (u *Updater) UpdateWithLock(ctx context.Context, toolsVersion string) (err error) { + // Create tools directory if it does not exist. + if err := os.MkdirAll(u.toolsDir, 0o755); err != nil { + return trace.Wrap(err) + } + // Lock concurrent client tools execution util requested version is updated. + unlock, err := utils.FSWriteLock(filepath.Join(u.toolsDir, lockFileName)) + if err != nil { + return trace.Wrap(err) + } + defer func() { + err = trace.NewAggregate(err, unlock()) + }() + + // If the version of the running binary or the version downloaded to + // tools directory is the same as the requested version of client tools, + // nothing to be done, exit early. + teleportVersion, err := checkToolVersion(u.toolsDir) + if err != nil && !trace.IsNotFound(err) { + return trace.Wrap(err) + + } + if toolsVersion == u.localVersion || toolsVersion == teleportVersion { + return nil + } + + // Download and update client tools in tools directory. + if err := u.Update(ctx, toolsVersion); err != nil { + return trace.Wrap(err) + } + + return +} + +// Update downloads requested version and replace it with existing one and cleanups the previous downloads +// with defined updater directory suffix. +func (u *Updater) Update(ctx context.Context, toolsVersion string) error { + // Get platform specific download URLs. + packages, err := teleportPackageURLs(u.baseURL, toolsVersion) + if err != nil { + return trace.Wrap(err) + } + + for _, pkg := range packages { + if err := u.update(ctx, pkg); err != nil { + return trace.Wrap(err) + } + } + + return nil +} + +// update downloads the archive and validate against the hash. Download to a +// temporary path within tools directory. +func (u *Updater) update(ctx context.Context, pkg packageURL) error { + hash, err := u.downloadHash(ctx, pkg.Hash) + if pkg.Optional && trace.IsNotFound(err) { + return nil + } + if err != nil { + return trace.Wrap(err) + } + + f, err := os.CreateTemp(u.toolsDir, "tmp-") + if err != nil { + return trace.Wrap(err) + } + defer func() { + _ = f.Close() + if err := os.Remove(f.Name()); err != nil { + slog.WarnContext(ctx, "failed to remove temporary archive file", "error", err) + } + }() + + archiveHash, err := u.downloadArchive(ctx, pkg.Archive, f) + if pkg.Optional && trace.IsNotFound(err) { + return nil + } + if err != nil { + return trace.Wrap(err) + } + if !bytes.Equal(archiveHash, hash) { + return trace.BadParameter("hash of archive does not match downloaded archive") + } + + pkgName := fmt.Sprint(uuid.New().String(), updatePackageSuffix) + extractDir := filepath.Join(u.toolsDir, pkgName) + if runtime.GOOS != constants.DarwinOS { + if err := os.Mkdir(extractDir, 0o755); err != nil { + return trace.Wrap(err) + } + } + + // Perform atomic replace so concurrent exec do not fail. + if err := packaging.ReplaceToolsBinaries(u.toolsDir, f.Name(), extractDir, u.tools); err != nil { + return trace.Wrap(err) + } + // Cleanup the tools directory with previously downloaded and un-archived versions. + if err := packaging.RemoveWithSuffix(u.toolsDir, updatePackageSuffix, pkgName); err != nil { + return trace.Wrap(err) + } + + return nil +} + +// Exec re-executes tool command with same arguments and environ variables. +func (u *Updater) Exec() (int, error) { + path, err := toolName(u.toolsDir) + if err != nil { + return 0, trace.Wrap(err) + } + // To prevent re-execution loop we have to disable update logic for re-execution. + env := append(os.Environ(), teleportToolsVersionEnv+"=off") + + if runtime.GOOS == constants.WindowsOS { + cmd := exec.Command(path, os.Args[1:]...) + cmd.Env = env + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return 0, trace.Wrap(err) + } + + return cmd.ProcessState.ExitCode(), nil + } + + if err := syscall.Exec(path, append([]string{path}, os.Args[1:]...), env); err != nil { + return 0, trace.Wrap(err) + } + + return 0, nil +} + +// downloadHash downloads the hash file `.sha256` for package checksum validation and return the hash sum. +func (u *Updater) downloadHash(ctx context.Context, url string) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, trace.Wrap(err) + } + resp, err := u.client.Do(req) + if err != nil { + return nil, trace.Wrap(err) + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + return nil, trace.NotFound("hash file is not found: %v", resp.StatusCode) + } + if resp.StatusCode != http.StatusOK { + return nil, trace.BadParameter("bad status when downloading archive hash: %v", resp.StatusCode) + } + + var buf bytes.Buffer + _, err = io.CopyN(&buf, resp.Body, sha256.Size*2) // SHA bytes to hex + if err != nil { + return nil, trace.Wrap(err) + } + hexBytes, err := hex.DecodeString(buf.String()) + if err != nil { + return nil, trace.Wrap(err) + } + + return hexBytes, nil +} + +// downloadArchive downloads the archive package by `url` and writes content to the writer interface, +// return calculated sha256 hash sum of the content. +func (u *Updater) downloadArchive(ctx context.Context, url string, f io.Writer) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, trace.Wrap(err) + } + resp, err := u.client.Do(req) + if err != nil { + return nil, trace.Wrap(err) + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + return nil, trace.NotFound("archive file is not found: %v", resp.StatusCode) + } + if resp.StatusCode != http.StatusOK { + return nil, trace.BadParameter("bad status when downloading archive: %v", resp.StatusCode) + } + + if resp.ContentLength != -1 { + if err := checkFreeSpace(u.toolsDir, uint64(resp.ContentLength)); err != nil { + return nil, trace.Wrap(err) + } + } + + h := sha256.New() + pw := &progressWriter{n: 0, limit: resp.ContentLength} + body := io.TeeReader(io.TeeReader(resp.Body, h), pw) + + // It is a little inefficient to download the file to disk and then re-load + // it into memory to unarchive later, but this is safer as it allows client + // tools to validate the hash before trying to operate on the archive. + _, err = io.CopyN(f, body, resp.ContentLength) + if err != nil { + return nil, trace.Wrap(err) + } + + return h.Sum(nil), nil +} diff --git a/lib/autoupdate/tools/utils.go b/lib/autoupdate/tools/utils.go new file mode 100644 index 0000000000000..d552b31abefe4 --- /dev/null +++ b/lib/autoupdate/tools/utils.go @@ -0,0 +1,175 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools + +import ( + "bufio" + "bytes" + "context" + "errors" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/coreos/go-semver/semver" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/modules" + "github.com/gravitational/teleport/lib/utils" +) + +// Dir returns the path to client tools in $TELEPORT_HOME/bin. +func Dir() (string, error) { + home := os.Getenv(types.HomeEnvVar) + if home == "" { + var err error + home, err = os.UserHomeDir() + if err != nil { + return "", trace.Wrap(err) + } + } + + return filepath.Join(home, ".tsh", "bin"), nil +} + +func checkToolVersion(toolsDir string) (string, error) { + // Find the path to the current executable. + path, err := toolName(toolsDir) + if err != nil { + return "", trace.Wrap(err) + } + if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { + return "", nil + } else if err != nil { + return "", trace.Wrap(err) + } + + // Set a timeout to not let "{tsh, tctl} version" block forever. Allow up + // to 10 seconds because sometimes MDM tools like Jamf cause a lot of + // latency in launching binaries. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Execute "{tsh, tctl} version" and pass in TELEPORT_TOOLS_VERSION=off to + // turn off all automatic updates code paths to prevent any recursion. + command := exec.CommandContext(ctx, path, "version") + command.Env = []string{teleportToolsVersionEnv + "=off"} + output, err := command.Output() + if err != nil { + return "", trace.Wrap(err) + } + + // The output for "{tsh, tctl} version" can be multiple lines. Find the + // actual version line and extract the version. + scanner := bufio.NewScanner(bytes.NewReader(output)) + for scanner.Scan() { + line := scanner.Text() + + if !strings.HasPrefix(line, "Teleport") { + continue + } + + matches := pattern.FindStringSubmatch(line) + if len(matches) != 2 { + return "", trace.BadParameter("invalid version line: %v", line) + } + version, err := semver.NewVersion(matches[1]) + if err != nil { + return "", trace.Wrap(err) + } + return version.String(), nil + } + + return "", trace.BadParameter("unable to determine version") +} + +// packageURL defines URLs to the archive and their archive sha256 hash file, and marks +// if this package is optional, for such case download needs to be ignored if package +// not found in CDN. +type packageURL struct { + Archive string + Hash string + Optional bool +} + +// teleportPackageURLs returns the URL for the Teleport archive to download. The format is: +// https://cdn.teleport.dev/teleport-{, ent-}v15.3.0-{linux, darwin, windows}-{amd64,arm64,arm,386}-{fips-}bin.tar.gz +func teleportPackageURLs(baseURL, toolsVersion string) ([]packageURL, error) { + switch runtime.GOOS { + case "darwin": + tsh := baseURL + "/tsh-" + toolsVersion + ".pkg" + teleport := baseURL + "/teleport-" + toolsVersion + ".pkg" + return []packageURL{ + {Archive: teleport, Hash: teleport + ".sha256"}, + {Archive: tsh, Hash: tsh + ".sha256", Optional: true}, + }, nil + case "windows": + archive := baseURL + "/teleport-v" + toolsVersion + "-windows-amd64-bin.zip" + return []packageURL{ + {Archive: archive, Hash: archive + ".sha256"}, + }, nil + case "linux": + m := modules.GetModules() + var b strings.Builder + b.WriteString(baseURL + "/teleport-") + if m.IsEnterpriseBuild() || m.IsBoringBinary() { + b.WriteString("ent-") + } + b.WriteString("v" + toolsVersion + "-" + runtime.GOOS + "-" + runtime.GOARCH + "-") + if m.IsBoringBinary() { + b.WriteString("fips-") + } + b.WriteString("bin.tar.gz") + archive := b.String() + return []packageURL{ + {Archive: archive, Hash: archive + ".sha256"}, + }, nil + default: + return nil, trace.BadParameter("unsupported runtime: %v", runtime.GOOS) + } +} + +// toolName returns the path to {tsh, tctl} for the executable that started +// the current process. +func toolName(toolsDir string) (string, error) { + executablePath, err := os.Executable() + if err != nil { + return "", trace.Wrap(err) + } + + return filepath.Join(toolsDir, filepath.Base(executablePath)), nil +} + +// checkFreeSpace verifies that we have enough requested space at specific directory. +func checkFreeSpace(path string, requested uint64) error { + free, err := utils.FreeDiskWithReserve(path, reservedFreeDisk) + if err != nil { + return trace.Errorf("failed to calculate free disk in %q: %v", path, err) + } + // Bail if there's not enough free disk space at the target. + if requested > free { + return trace.Errorf("%q needs %d additional bytes of disk space", path, requested-free) + } + + return nil +} diff --git a/lib/utils/disk.go b/lib/utils/disk.go index 9e2419527051a..782c598d26229 100644 --- a/lib/utils/disk.go +++ b/lib/utils/disk.go @@ -53,10 +53,8 @@ func FreeDiskWithReserve(dir string, reservedFreeDisk uint64) (uint64, error) { if err != nil { return 0, trace.Wrap(err) } - if stat.Bsize < 0 { - return 0, trace.Errorf("invalid size") - } - avail := stat.Bavail * uint64(stat.Bsize) + //nolint:unconvert // The cast is only necessary for linux platform. + avail := uint64(stat.Bavail) * uint64(stat.Bsize) if reservedFreeDisk > avail { return 0, trace.Errorf("no free space left") } diff --git a/lib/utils/packaging/unarchive_unix.go b/lib/utils/packaging/unarchive_unix.go index 3be7d0c473ef9..ea51afdbbc7f0 100644 --- a/lib/utils/packaging/unarchive_unix.go +++ b/lib/utils/packaging/unarchive_unix.go @@ -33,6 +33,8 @@ import ( "github.com/google/renameio/v2" "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/constants" ) // ReplaceToolsBinaries extracts executables specified by execNames from archivePath into @@ -43,7 +45,7 @@ import ( // For other POSIX, archivePath must be a gzipped tarball. func ReplaceToolsBinaries(toolsDir string, archivePath string, extractDir string, execNames []string) error { switch runtime.GOOS { - case "darwin": + case constants.DarwinOS: return replacePkg(toolsDir, archivePath, extractDir, execNames) default: return replaceTarGz(toolsDir, archivePath, extractDir, execNames) From 75ed24e589de062a51967553eb3a9e31ce934546 Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Thu, 31 Oct 2024 13:09:24 -0400 Subject: [PATCH 5/9] Client auto updates integration for {tctl,tsh} (#47815) * Client auto updates integration for tctl/tsh * Add version validation Fix recursive version check for darwin platform Fix cleanup for multi-package support * Fix identifying tools removal from home directory * Replace ToolsMode with ToolsAutoUpdate * Reuse insecure flag for tests * Fix CheckRemote with login * Fix windows administrative access requirement Update must be able to be canceled, re-execute with latest version or last updated Show progress bar before request is made * Fix update cancellation for login action Address review comments * Add signal handler with stack context cancellation * Use copy instead of hard link for windows Fix progress bar if we can't receive size of package * Replace with list in order to support manual cancel * Download archive package to temp directory * Decrease timeout for client tools proxy call --- api/client/webclient/webclient.go | 4 +- integration/autoupdate/tools/main_test.go | 17 +-- integration/autoupdate/tools/updater/main.go | 21 +--- integration/autoupdate/tools/updater_test.go | 17 +-- .../{archive.go => archive/packaging.go} | 2 +- integrations/terraform/go.mod | 1 + integrations/terraform/go.sum | 1 - lib/autoupdate/tools/progress.go | 51 ++++++-- lib/autoupdate/tools/updater.go | 118 ++++++++++-------- lib/autoupdate/tools/utils.go | 19 ++- lib/client/api.go | 35 ++++++ lib/utils/packaging/unarchive.go | 16 ++- lib/utils/packaging/unarchive_test.go | 10 +- lib/utils/packaging/unarchive_unix.go | 5 +- lib/utils/signal/stack_handler.go | 98 +++++++++++++++ lib/utils/signal/stack_handler_test.go | 88 +++++++++++++ lib/web/apiserver.go | 94 +++++++++++--- lib/web/apiserver_ping_test.go | 51 ++++++-- tool/tctl/common/tctl.go | 41 +++++- tool/tctl/main.go | 8 +- tool/tsh/common/tsh.go | 104 ++++++++++++++- 21 files changed, 650 insertions(+), 151 deletions(-) rename integration/helpers/{archive.go => archive/packaging.go} (99%) create mode 100644 lib/utils/signal/stack_handler.go create mode 100644 lib/utils/signal/stack_handler_test.go diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index ba959b20a772f..2942330c35456 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -335,8 +335,8 @@ type ProxySettings struct { type AutoUpdateSettings struct { // ToolsVersion defines the version of {tsh, tctl} for client auto update. ToolsVersion string `json:"tools_version"` - // ToolsMode defines mode client auto update feature `enabled|disabled`. - ToolsMode string `json:"tools_mode"` + // ToolsAutoUpdate indicates if the requesting tools client should be updated. + ToolsAutoUpdate bool `json:"tools_auto_update"` } // KubeProxySettings is kubernetes proxy settings diff --git a/integration/autoupdate/tools/main_test.go b/integration/autoupdate/tools/main_test.go index a14a6dc9fc683..bbc3f559f65c0 100644 --- a/integration/autoupdate/tools/main_test.go +++ b/integration/autoupdate/tools/main_test.go @@ -37,7 +37,8 @@ import ( "github.com/gravitational/trace" - "github.com/gravitational/teleport/integration/helpers" + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/integration/helpers/archive" ) const ( @@ -133,9 +134,9 @@ func buildAndArchiveApps(ctx context.Context, path string, toolsDir string, vers for _, app := range []string{"tsh", "tctl"} { output := filepath.Join(versionPath, app) switch runtime.GOOS { - case "windows": + case constants.WindowsOS: output = filepath.Join(versionPath, app+".exe") - case "darwin": + case constants.DarwinOS: output = filepath.Join(versionPath, app+".app", "Contents", "MacOS", app) } if err := buildBinary(output, toolsDir, version, baseURL); err != nil { @@ -143,15 +144,15 @@ func buildAndArchiveApps(ctx context.Context, path string, toolsDir string, vers } } switch runtime.GOOS { - case "darwin": + case constants.DarwinOS: archivePath := filepath.Join(path, fmt.Sprintf("teleport-%s.pkg", version)) - return trace.Wrap(helpers.CompressDirToPkgFile(ctx, versionPath, archivePath, "com.example.pkgtest")) - case "windows": + return trace.Wrap(archive.CompressDirToPkgFile(ctx, versionPath, archivePath, "com.example.pkgtest")) + case constants.WindowsOS: archivePath := filepath.Join(path, fmt.Sprintf("teleport-v%s-windows-amd64-bin.zip", version)) - return trace.Wrap(helpers.CompressDirToZipFile(ctx, versionPath, archivePath)) + return trace.Wrap(archive.CompressDirToZipFile(ctx, versionPath, archivePath)) default: archivePath := filepath.Join(path, fmt.Sprintf("teleport-v%s-linux-%s-bin.tar.gz", version, runtime.GOARCH)) - return trace.Wrap(helpers.CompressDirToTarGzFile(ctx, versionPath, archivePath)) + return trace.Wrap(archive.CompressDirToTarGzFile(ctx, versionPath, archivePath)) } } diff --git a/integration/autoupdate/tools/updater/main.go b/integration/autoupdate/tools/updater/main.go index e14c76e5d5aa8..775c7ab7b2e9d 100644 --- a/integration/autoupdate/tools/updater/main.go +++ b/integration/autoupdate/tools/updater/main.go @@ -25,11 +25,9 @@ import ( "log" "os" "os/signal" - "runtime" "syscall" "time" - "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/lib/autoupdate/tools" ) @@ -40,17 +38,20 @@ var ( ) func main() { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() ctx, _ = signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) updater := tools.NewUpdater( - clientTools(), + tools.DefaultClientTools(), toolsDir, version, tools.WithBaseURL(baseURL), ) - toolsVersion, reExec := updater.CheckLocal() + toolsVersion, reExec, err := updater.CheckLocal() + if err != nil { + log.Fatal(err) + } if reExec { // Download and update the version of client tools required by the cluster. // This is required if the user passed in the TELEPORT_TOOLS_VERSION explicitly. @@ -76,13 +77,3 @@ func main() { fmt.Printf("Teleport v%v git\n", version) } } - -// clientTools list of the client tools needs to be updated. -func clientTools() []string { - switch runtime.GOOS { - case constants.WindowsOS: - return []string{"tsh.exe", "tctl.exe"} - default: - return []string{"tsh", "tctl"} - } -} diff --git a/integration/autoupdate/tools/updater_test.go b/integration/autoupdate/tools/updater_test.go index 96d5486462067..20660f72f06e2 100644 --- a/integration/autoupdate/tools/updater_test.go +++ b/integration/autoupdate/tools/updater_test.go @@ -26,7 +26,6 @@ import ( "os/exec" "path/filepath" "regexp" - "runtime" "strings" "testing" "time" @@ -34,7 +33,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/lib/autoupdate/tools" ) @@ -51,7 +49,7 @@ func TestUpdate(t *testing.T) { // Fetch compiled test binary with updater logic and install to $TELEPORT_HOME. updater := tools.NewUpdater( - clientTools(), + tools.DefaultClientTools(), toolsDir, testVersions[0], tools.WithBaseURL(baseURL), @@ -93,7 +91,7 @@ func TestParallelUpdate(t *testing.T) { // Initial fetch the updater binary un-archive and replace. updater := tools.NewUpdater( - clientTools(), + tools.DefaultClientTools(), toolsDir, testVersions[0], tools.WithBaseURL(baseURL), @@ -167,7 +165,7 @@ func TestUpdateInterruptSignal(t *testing.T) { // Initial fetch the updater binary un-archive and replace. updater := tools.NewUpdater( - clientTools(), + tools.DefaultClientTools(), toolsDir, testVersions[0], tools.WithBaseURL(baseURL), @@ -220,12 +218,3 @@ func TestUpdateInterruptSignal(t *testing.T) { } assert.Contains(t, output.String(), "Update progress:") } - -func clientTools() []string { - switch runtime.GOOS { - case constants.WindowsOS: - return []string{"tsh.exe", "tctl.exe"} - default: - return []string{"tsh", "tctl"} - } -} diff --git a/integration/helpers/archive.go b/integration/helpers/archive/packaging.go similarity index 99% rename from integration/helpers/archive.go rename to integration/helpers/archive/packaging.go index 6e48108013d86..ee237749115a3 100644 --- a/integration/helpers/archive.go +++ b/integration/helpers/archive/packaging.go @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package helpers +package archive import ( "archive/tar" diff --git a/integrations/terraform/go.mod b/integrations/terraform/go.mod index 4e45558f25c33..d7354dbea74ff 100644 --- a/integrations/terraform/go.mod +++ b/integrations/terraform/go.mod @@ -167,6 +167,7 @@ require ( github.com/google/go-tpm-tools v0.4.4 // indirect github.com/google/go-tspi v0.3.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/renameio/v2 v2.0.0 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index f55371fc300f6..5043f2d8f7d63 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -612,7 +612,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= diff --git a/lib/autoupdate/tools/progress.go b/lib/autoupdate/tools/progress.go index 95395003730ec..34bbad2ff888d 100644 --- a/lib/autoupdate/tools/progress.go +++ b/lib/autoupdate/tools/progress.go @@ -20,24 +20,59 @@ package tools import ( "fmt" + "io" "strings" + + "github.com/gravitational/trace" ) type progressWriter struct { - n int64 - limit int64 + n int64 + limit int64 + size int + progress int } -func (w *progressWriter) Write(p []byte) (int, error) { - w.n = w.n + int64(len(p)) +// newProgressWriter creates progress writer instance and prints empty +// progress bar right after initialisation. +func newProgressWriter(size int) (*progressWriter, func()) { + pw := &progressWriter{size: size} + pw.Print(0) + return pw, func() { + fmt.Print("\n") + } +} - n := int((w.n*100)/w.limit) / 10 - bricks := strings.Repeat("â–’", n) + strings.Repeat(" ", 10-n) +// Print prints the update progress bar with `n` bricks. +func (w *progressWriter) Print(n int) { + bricks := strings.Repeat("â–’", n) + strings.Repeat(" ", w.size-n) fmt.Print("\rUpdate progress: [" + bricks + "] (Ctrl-C to cancel update)") +} - if w.n == w.limit { - fmt.Print("\n") +func (w *progressWriter) Write(p []byte) (int, error) { + if w.limit == 0 || w.size == 0 { + return len(p), nil + } + + w.n += int64(len(p)) + bricks := int((w.n*100)/w.limit) / w.size + if w.progress != bricks { + w.Print(bricks) + w.progress = bricks } return len(p), nil } + +// CopyLimit sets the limit of writing bytes to the progress writer and initiate copying process. +func (w *progressWriter) CopyLimit(dst io.Writer, src io.Reader, limit int64) (written int64, err error) { + if limit < 0 { + n, err := io.Copy(dst, io.TeeReader(src, w)) + w.Print(w.size) + return n, trace.Wrap(err) + } + + w.limit = limit + n, err := io.CopyN(dst, io.TeeReader(src, w), limit) + return n, trace.Wrap(err) +} diff --git a/lib/autoupdate/tools/updater.go b/lib/autoupdate/tools/updater.go index 96991044ccc31..96352e34d9910 100644 --- a/lib/autoupdate/tools/updater.go +++ b/lib/autoupdate/tools/updater.go @@ -36,12 +36,12 @@ import ( "syscall" "time" + "github.com/coreos/go-semver/semver" "github.com/google/uuid" "github.com/gravitational/trace" "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" - "github.com/gravitational/teleport/api/types/autoupdate" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/packaging" ) @@ -114,30 +114,37 @@ func NewUpdater(tools []string, toolsDir string, localVersion string, options .. // CheckLocal is run at client tool startup and will only perform local checks. // Returns the version needs to be updated and re-executed, by re-execution flag we // understand that update and re-execute is required. -func (u *Updater) CheckLocal() (version string, reExec bool) { +func (u *Updater) CheckLocal() (version string, reExec bool, err error) { // Check if the user has requested a specific version of client tools. requestedVersion := os.Getenv(teleportToolsVersionEnv) switch requestedVersion { // The user has turned off any form of automatic updates. case "off": - return "", false + return "", false, nil // Requested version already the same as client version. case u.localVersion: - return u.localVersion, false + return u.localVersion, false, nil + // No requested version, we continue. + case "": + // Requested version that is not the local one. + default: + if _, err := semver.NewVersion(requestedVersion); err != nil { + return "", false, trace.Wrap(err, "checking that request version is semantic") + } + return requestedVersion, true, nil } // If a version of client tools has already been downloaded to // tools directory, return that. - toolsVersion, err := checkToolVersion(u.toolsDir) - if err != nil { - return "", false + toolsVersion, err := CheckToolVersion(u.toolsDir) + if trace.IsNotFound(err) { + return u.localVersion, false, nil } - // The user has requested a specific version of client tools. - if requestedVersion != "" && requestedVersion != toolsVersion { - return requestedVersion, true + if err != nil { + return "", false, trace.Wrap(err) } - return toolsVersion, false + return toolsVersion, true, nil } // CheckRemote first checks the version set by the environment variable. If not set or disabled, @@ -145,7 +152,7 @@ func (u *Updater) CheckLocal() (version string, reExec bool) { // the `webapi/find` handler, which stores information about the required client tools version to // operate with this cluster. It returns the semantic version that needs updating and whether // re-execution is necessary, by re-execution flag we understand that update and re-execute is required. -func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string) (version string, reExec bool, err error) { +func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string, insecure bool) (version string, reExec bool, err error) { // Check if the user has requested a specific version of client tools. requestedVersion := os.Getenv(teleportToolsVersionEnv) switch requestedVersion { @@ -155,6 +162,14 @@ func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string) (version st // Requested version already the same as client version. case u.localVersion: return u.localVersion, false, nil + // No requested version, we continue. + case "": + // Requested version that is not the local one. + default: + if _, err := semver.NewVersion(requestedVersion); err != nil { + return "", false, trace.Wrap(err, "checking that request version is semantic") + } + return requestedVersion, true, nil } certPool, err := x509.SystemCertPool() @@ -165,7 +180,8 @@ func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string) (version st Context: ctx, ProxyAddr: proxyAddr, Pool: certPool, - Timeout: 30 * time.Second, + Timeout: 10 * time.Second, + Insecure: insecure, }) if err != nil { return "", false, trace.Wrap(err) @@ -173,28 +189,28 @@ func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string) (version st // If a version of client tools has already been downloaded to // tools directory, return that. - toolsVersion, err := checkToolVersion(u.toolsDir) - if err != nil { + toolsVersion, err := CheckToolVersion(u.toolsDir) + if err != nil && !trace.IsNotFound(err) { return "", false, trace.Wrap(err) } switch { - case requestedVersion != "" && requestedVersion != toolsVersion: - return requestedVersion, true, nil - case resp.AutoUpdate.ToolsMode != autoupdate.ToolsUpdateModeEnabled || resp.AutoUpdate.ToolsVersion == "": - return "", false, nil + case !resp.AutoUpdate.ToolsAutoUpdate || resp.AutoUpdate.ToolsVersion == "": + if toolsVersion == "" { + return u.localVersion, false, nil + } case u.localVersion == resp.AutoUpdate.ToolsVersion: - return resp.AutoUpdate.ToolsVersion, false, nil + return u.localVersion, false, nil case resp.AutoUpdate.ToolsVersion != toolsVersion: return resp.AutoUpdate.ToolsVersion, true, nil } - return toolsVersion, false, nil + return toolsVersion, true, nil } // UpdateWithLock acquires filesystem lock, downloads requested version package, // unarchive and replace existing one. -func (u *Updater) UpdateWithLock(ctx context.Context, toolsVersion string) (err error) { +func (u *Updater) UpdateWithLock(ctx context.Context, updateToolsVersion string) (err error) { // Create tools directory if it does not exist. if err := os.MkdirAll(u.toolsDir, 0o755); err != nil { return trace.Wrap(err) @@ -211,21 +227,20 @@ func (u *Updater) UpdateWithLock(ctx context.Context, toolsVersion string) (err // If the version of the running binary or the version downloaded to // tools directory is the same as the requested version of client tools, // nothing to be done, exit early. - teleportVersion, err := checkToolVersion(u.toolsDir) + toolsVersion, err := CheckToolVersion(u.toolsDir) if err != nil && !trace.IsNotFound(err) { return trace.Wrap(err) - } - if toolsVersion == u.localVersion || toolsVersion == teleportVersion { + if updateToolsVersion == toolsVersion { return nil } // Download and update client tools in tools directory. - if err := u.Update(ctx, toolsVersion); err != nil { + if err := u.Update(ctx, updateToolsVersion); err != nil { return trace.Wrap(err) } - return + return nil } // Update downloads requested version and replace it with existing one and cleanups the previous downloads @@ -237,10 +252,18 @@ func (u *Updater) Update(ctx context.Context, toolsVersion string) error { return trace.Wrap(err) } + var pkgNames []string for _, pkg := range packages { - if err := u.update(ctx, pkg); err != nil { + pkgName := fmt.Sprint(uuid.New().String(), updatePackageSuffix) + if err := u.update(ctx, pkg, pkgName); err != nil { return trace.Wrap(err) } + pkgNames = append(pkgNames, pkgName) + } + + // Cleanup the tools directory with previously downloaded and un-archived versions. + if err := packaging.RemoveWithSuffix(u.toolsDir, updatePackageSuffix, pkgNames); err != nil { + slog.WarnContext(ctx, "failed to clean up tools directory", "error", err) } return nil @@ -248,16 +271,8 @@ func (u *Updater) Update(ctx context.Context, toolsVersion string) error { // update downloads the archive and validate against the hash. Download to a // temporary path within tools directory. -func (u *Updater) update(ctx context.Context, pkg packageURL) error { - hash, err := u.downloadHash(ctx, pkg.Hash) - if pkg.Optional && trace.IsNotFound(err) { - return nil - } - if err != nil { - return trace.Wrap(err) - } - - f, err := os.CreateTemp(u.toolsDir, "tmp-") +func (u *Updater) update(ctx context.Context, pkg packageURL, pkgName string) error { + f, err := os.CreateTemp("", "teleport-") if err != nil { return trace.Wrap(err) } @@ -275,11 +290,19 @@ func (u *Updater) update(ctx context.Context, pkg packageURL) error { if err != nil { return trace.Wrap(err) } + + hash, err := u.downloadHash(ctx, pkg.Hash) + if pkg.Optional && trace.IsNotFound(err) { + return nil + } + if err != nil { + return trace.Wrap(err) + } + if !bytes.Equal(archiveHash, hash) { return trace.BadParameter("hash of archive does not match downloaded archive") } - pkgName := fmt.Sprint(uuid.New().String(), updatePackageSuffix) extractDir := filepath.Join(u.toolsDir, pkgName) if runtime.GOOS != constants.DarwinOS { if err := os.Mkdir(extractDir, 0o755); err != nil { @@ -291,10 +314,6 @@ func (u *Updater) update(ctx context.Context, pkg packageURL) error { if err := packaging.ReplaceToolsBinaries(u.toolsDir, f.Name(), extractDir, u.tools); err != nil { return trace.Wrap(err) } - // Cleanup the tools directory with previously downloaded and un-archived versions. - if err := packaging.RemoveWithSuffix(u.toolsDir, updatePackageSuffix, pkgName); err != nil { - return trace.Wrap(err) - } return nil } @@ -340,7 +359,7 @@ func (u *Updater) downloadHash(ctx context.Context, url string) ([]byte, error) } defer resp.Body.Close() if resp.StatusCode == http.StatusNotFound { - return nil, trace.NotFound("hash file is not found: %v", resp.StatusCode) + return nil, trace.NotFound("hash file is not found: %q", url) } if resp.StatusCode != http.StatusOK { return nil, trace.BadParameter("bad status when downloading archive hash: %v", resp.StatusCode) @@ -362,6 +381,11 @@ func (u *Updater) downloadHash(ctx context.Context, url string) ([]byte, error) // downloadArchive downloads the archive package by `url` and writes content to the writer interface, // return calculated sha256 hash sum of the content. func (u *Updater) downloadArchive(ctx context.Context, url string, f io.Writer) ([]byte, error) { + // Display a progress bar before initiating the update request to inform the user that + // an update is in progress, allowing them the option to cancel before actual response + // which might be delayed with slow internet connection or complete isolation to CDN. + pw, finish := newProgressWriter(10) + defer finish() req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, trace.Wrap(err) @@ -385,14 +409,10 @@ func (u *Updater) downloadArchive(ctx context.Context, url string, f io.Writer) } h := sha256.New() - pw := &progressWriter{n: 0, limit: resp.ContentLength} - body := io.TeeReader(io.TeeReader(resp.Body, h), pw) - // It is a little inefficient to download the file to disk and then re-load // it into memory to unarchive later, but this is safer as it allows client // tools to validate the hash before trying to operate on the archive. - _, err = io.CopyN(f, body, resp.ContentLength) - if err != nil { + if _, err := pw.CopyLimit(f, io.TeeReader(resp.Body, h), resp.ContentLength); err != nil { return nil, trace.Wrap(err) } diff --git a/lib/autoupdate/tools/utils.go b/lib/autoupdate/tools/utils.go index d552b31abefe4..f937d228b5cd4 100644 --- a/lib/autoupdate/tools/utils.go +++ b/lib/autoupdate/tools/utils.go @@ -33,6 +33,7 @@ import ( "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" + "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/utils" @@ -52,14 +53,26 @@ func Dir() (string, error) { return filepath.Join(home, ".tsh", "bin"), nil } -func checkToolVersion(toolsDir string) (string, error) { +// DefaultClientTools list of the client tools needs to be updated by default. +func DefaultClientTools() []string { + switch runtime.GOOS { + case constants.WindowsOS: + return []string{"tsh.exe", "tctl.exe"} + default: + return []string{"tsh", "tctl"} + } +} + +// CheckToolVersion returns current installed client tools version, must return NotFoundError if +// the client tools is not found in tools directory. +func CheckToolVersion(toolsDir string) (string, error) { // Find the path to the current executable. path, err := toolName(toolsDir) if err != nil { return "", trace.Wrap(err) } if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { - return "", nil + return "", trace.NotFound("autoupdate tool not found in %q", toolsDir) } else if err != nil { return "", trace.Wrap(err) } @@ -76,7 +89,7 @@ func checkToolVersion(toolsDir string) (string, error) { command.Env = []string{teleportToolsVersionEnv + "=off"} output, err := command.Output() if err != nil { - return "", trace.Wrap(err) + return "", trace.WrapWithMessage(err, "failed to determine version of %q tool", path) } // The output for "{tsh, tctl} version" can be multiple lines. Find the diff --git a/lib/client/api.go b/lib/client/api.go index 4337bcd241632..9eae61ca71277 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -73,6 +73,7 @@ import ( "github.com/gravitational/teleport/lib/auth/touchid" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/autoupdate/tools" libmfa "github.com/gravitational/teleport/lib/client/mfa" "github.com/gravitational/teleport/lib/client/terminal" "github.com/gravitational/teleport/lib/defaults" @@ -92,6 +93,7 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/agentconn" "github.com/gravitational/teleport/lib/utils/proxy" + "github.com/gravitational/teleport/lib/utils/signal" ) const ( @@ -668,6 +670,39 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error, return trace.Wrap(err) } + // The user has typed a command like `tsh ssh ...` without being logged in, + // if the running binary needs to be updated, update and re-exec. + // + // If needed, download the new version of {tsh, tctl} and re-exec. Make + // sure to exit this process with the same exit code as the child process. + // + toolsDir, err := tools.Dir() + if err != nil { + return trace.Wrap(err) + } + updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version) + toolsVersion, reExec, err := updater.CheckRemote(ctx, tc.WebProxyAddr, tc.InsecureSkipVerify) + if err != nil { + return trace.Wrap(err) + } + if reExec { + ctxUpdate, cancel := signal.GetSignalHandler().NotifyContext(context.Background()) + defer cancel() + // Download the version of client tools required by the cluster. + err := updater.UpdateWithLock(ctxUpdate, toolsVersion) + if err != nil && !errors.Is(err, context.Canceled) { + utils.FatalError(err) + } + // Re-execute client tools with the correct version of client tools. + code, err := updater.Exec() + if err != nil && !errors.Is(err, os.ErrNotExist) { + log.Debugf("Failed to re-exec client tool: %v.", err) + os.Exit(code) + } else if err == nil { + os.Exit(code) + } + } + return fn() } diff --git a/lib/utils/packaging/unarchive.go b/lib/utils/packaging/unarchive.go index f1a197e095b1a..6496afbc182c3 100644 --- a/lib/utils/packaging/unarchive.go +++ b/lib/utils/packaging/unarchive.go @@ -38,13 +38,13 @@ const ( ) // RemoveWithSuffix removes all that matches the provided suffix, except for file or directory with `skipName`. -func RemoveWithSuffix(dir, suffix, skipName string) error { +func RemoveWithSuffix(dir, suffix string, skipNames []string) error { var removePaths []string err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return trace.Wrap(err) } - if skipName == info.Name() { + if slices.Contains(skipNames, info.Name()) { return nil } if !strings.HasSuffix(info.Name(), suffix) { @@ -59,12 +59,13 @@ func RemoveWithSuffix(dir, suffix, skipName string) error { if err != nil { return trace.Wrap(err) } + var aggErr []error for _, path := range removePaths { if err := os.RemoveAll(path); err != nil { - return trace.Wrap(err) + aggErr = append(aggErr, err) } } - return nil + return trace.NewAggregate(aggErr...) } // replaceZip un-archives the Teleport package in .zip format, iterates through @@ -118,7 +119,7 @@ func replaceZip(toolsDir string, archivePath string, extractDir string, execName defer file.Close() dest := filepath.Join(extractDir, baseName) - destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755) if err != nil { return trace.Wrap(err) } @@ -131,7 +132,10 @@ func replaceZip(toolsDir string, archivePath string, extractDir string, execName if err := os.Remove(appPath); err != nil && !os.IsNotExist(err) { return trace.Wrap(err) } - if err := os.Symlink(dest, appPath); err != nil { + // For the Windows build we have to copy binary to be able + // to do this without administrative access as it required + // for symlinks. + if err := utils.CopyFile(dest, appPath, 0o755); err != nil { return trace.Wrap(err) } return trace.Wrap(destFile.Close()) diff --git a/lib/utils/packaging/unarchive_test.go b/lib/utils/packaging/unarchive_test.go index 30933bbb75927..b124b603b0fd5 100644 --- a/lib/utils/packaging/unarchive_test.go +++ b/lib/utils/packaging/unarchive_test.go @@ -30,7 +30,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/gravitational/teleport/integration/helpers" + "github.com/gravitational/teleport/integration/helpers/archive" ) // TestPackaging verifies un-archiving of all supported teleport package formats. @@ -63,7 +63,7 @@ func TestPackaging(t *testing.T) { t.Run("tar.gz", func(t *testing.T) { archivePath := filepath.Join(toolsDir, "tsh.tar.gz") - err = helpers.CompressDirToTarGzFile(ctx, sourceDir, archivePath) + err = archive.CompressDirToTarGzFile(ctx, sourceDir, archivePath) require.NoError(t, err) require.FileExists(t, archivePath, "archive not created") @@ -85,7 +85,7 @@ func TestPackaging(t *testing.T) { t.Skip("unsupported platform") } archivePath := filepath.Join(toolsDir, "tsh.pkg") - err = helpers.CompressDirToPkgFile(ctx, sourceDir, archivePath, "com.example.pkgtest") + err = archive.CompressDirToPkgFile(ctx, sourceDir, archivePath, "com.example.pkgtest") require.NoError(t, err) require.FileExists(t, archivePath, "archive not created") @@ -101,7 +101,7 @@ func TestPackaging(t *testing.T) { t.Run("zip", func(t *testing.T) { archivePath := filepath.Join(toolsDir, "tsh.zip") - err = helpers.CompressDirToZipFile(ctx, sourceDir, archivePath) + err = archive.CompressDirToZipFile(ctx, sourceDir, archivePath) require.NoError(t, err) require.FileExists(t, archivePath, "archive not created") @@ -132,7 +132,7 @@ func TestRemoveWithSuffix(t *testing.T) { dirInSkipPath := filepath.Join(skipPath, dirForRemove) require.NoError(t, os.MkdirAll(skipPath, 0o755)) - err := RemoveWithSuffix(testDir, dirForRemove, skipName) + err := RemoveWithSuffix(testDir, dirForRemove, []string{skipName}) require.NoError(t, err) _, err = os.Stat(filepath.Join(testDir, dirForRemove)) diff --git a/lib/utils/packaging/unarchive_unix.go b/lib/utils/packaging/unarchive_unix.go index ea51afdbbc7f0..8daf1b3aa5525 100644 --- a/lib/utils/packaging/unarchive_unix.go +++ b/lib/utils/packaging/unarchive_unix.go @@ -186,9 +186,10 @@ func replacePkg(toolsDir string, archivePath string, extractDir string, execName // swap operations. This ensures that the "com.apple.macl" extended // attribute is set and macOS will not send a SIGKILL to the process // if multiple processes are trying to operate on it. - command := exec.Command(path, "version", "--client") + command := exec.Command(path, "version") + command.Env = []string{"TELEPORT_TOOLS_VERSION=off"} if err := command.Run(); err != nil { - return trace.Wrap(err) + return trace.WrapWithMessage(err, "failed to validate binary") } // Due to macOS applications not being a single binary (they are a diff --git a/lib/utils/signal/stack_handler.go b/lib/utils/signal/stack_handler.go new file mode 100644 index 0000000000000..0fcbefb081d11 --- /dev/null +++ b/lib/utils/signal/stack_handler.go @@ -0,0 +1,98 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package signal + +import ( + "container/list" + "context" + "os" + "os/signal" + "sync" + "syscall" +) + +// Handler implements stack for context cancellation. +type Handler struct { + mu sync.Mutex + list *list.List +} + +var handler = &Handler{ + list: list.New(), +} + +// GetSignalHandler returns global singleton instance of signal +func GetSignalHandler() *Handler { + return handler +} + +// NotifyContext creates context which going to be canceled after SIGINT, SIGTERM +// in order of adding them to the stack. When very first context is canceled +// we stop watching the OS signals. +func (s *Handler) NotifyContext(parent context.Context) (context.Context, context.CancelFunc) { + s.mu.Lock() + defer s.mu.Unlock() + + if s.list.Len() == 0 { + s.listenSignals() + } + + ctx, cancel := context.WithCancel(parent) + element := s.list.PushBack(cancel) + + return ctx, func() { + s.mu.Lock() + defer s.mu.Unlock() + + s.list.Remove(element) + cancel() + } +} + +// listenSignals sets up the signal listener for SIGINT, SIGTERM. +func (s *Handler) listenSignals() { + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + go func() { + for { + if sig := <-sigChan; sig == nil { + return + } + if !s.cancelNext() { + signal.Stop(sigChan) + return + } + } + }() +} + +// cancelNext calls the most recent cancel func in the stack. +func (s *Handler) cancelNext() bool { + s.mu.Lock() + defer s.mu.Unlock() + + if s.list.Len() > 0 { + cancel := s.list.Remove(s.list.Back()) + if cancel != nil { + cancel.(context.CancelFunc)() + } + } + + return s.list.Len() != 0 +} diff --git a/lib/utils/signal/stack_handler_test.go b/lib/utils/signal/stack_handler_test.go new file mode 100644 index 0000000000000..b900939b886e8 --- /dev/null +++ b/lib/utils/signal/stack_handler_test.go @@ -0,0 +1,88 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package signal + +import ( + "context" + "os" + "syscall" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestGetSignalHandler verifies the cancellation stack order. +func TestGetSignalHandler(t *testing.T) { + testHandler := GetSignalHandler() + parent := context.Background() + + ctx1, cancel1 := testHandler.NotifyContext(parent) + ctx2, cancel2 := testHandler.NotifyContext(parent) + ctx3, cancel3 := testHandler.NotifyContext(parent) + ctx4, cancel4 := testHandler.NotifyContext(parent) + t.Cleanup(func() { + cancel4() + cancel2() + cancel1() + }) + + // Verify that all context not canceled. + require.NoError(t, ctx4.Err()) + require.NoError(t, ctx3.Err()) + require.NoError(t, ctx2.Err()) + require.NoError(t, ctx1.Err()) + + // Cancel context manually to ensure it was removed from stack in right order. + cancel3() + + // Check that last added context is canceled. + require.NoError(t, syscall.Kill(os.Getpid(), syscall.SIGINT)) + select { + case <-ctx4.Done(): + assert.ErrorIs(t, ctx3.Err(), context.Canceled) + assert.NoError(t, ctx2.Err()) + assert.NoError(t, ctx1.Err()) + case <-time.After(time.Second): + assert.Fail(t, "context 3 must be canceled") + } + + // Send interrupt signal to cancel next context in the stack. + require.NoError(t, syscall.Kill(os.Getpid(), syscall.SIGINT)) + select { + case <-ctx2.Done(): + assert.ErrorIs(t, ctx4.Err(), context.Canceled) + assert.ErrorIs(t, ctx3.Err(), context.Canceled) + assert.NoError(t, ctx1.Err()) + case <-time.After(time.Second): + assert.Fail(t, "context 2 must be canceled") + } + + // All context must be canceled. + require.NoError(t, syscall.Kill(os.Getpid(), syscall.SIGINT)) + select { + case <-ctx1.Done(): + assert.ErrorIs(t, ctx4.Err(), context.Canceled) + assert.ErrorIs(t, ctx3.Err(), context.Canceled) + assert.ErrorIs(t, ctx2.Err(), context.Canceled) + case <-time.After(time.Second): + assert.Fail(t, "context 1 must be canceled") + } +} diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index dca0512e948c1..efb98f79a6d89 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -49,22 +49,26 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/julienschmidt/httprouter" - "github.com/sashabaranov/go-openai" "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" oteltrace "go.opentelemetry.io/otel/trace" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" "golang.org/x/crypto/ssh" "golang.org/x/mod/semver" - "golang.org/x/time/rate" "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/timestamppb" "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api" + "github.com/gravitational/teleport/api/types/autoupdate" apiclient "github.com/gravitational/teleport/api/client" + "github.com/sashabaranov/go-openai" + "golang.org/x/time/rate" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" + autoupdatepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" "github.com/gravitational/teleport/api/mfa" apitracing "github.com/gravitational/teleport/api/observability/tracing" "github.com/gravitational/teleport/api/types" @@ -130,6 +134,10 @@ const ( // DefaultFeatureWatchInterval is the default time in which the feature watcher // should ping the auth server to check for updated features DefaultFeatureWatchInterval = time.Minute * 5 + // findEndpointCacheTTL is the cache TTL for the find endpoint generic answer. + // This cache is here to protect against accidental or intentional DDoS, the TTL must be low to quickly reflect + // cluster configuration changes. + findEndpointCacheTTL = 10 * time.Second ) // healthCheckAppServerFunc defines a function used to perform a health check @@ -179,6 +187,11 @@ type Handler struct { // an authenticated websocket so unauthenticated sockets dont get left // open. wsIODeadline time.Duration + + // findEndpointCache is used to cache the find endpoint answer. As this endpoint is unprotected and has high + // rate-limits, each call must cause minimal work. The cached answer can be modulated after, for example if the + // caller specified its Automatic Updates UUID or group. + findEndpointCache *utils.FnCache } // HandlerOption is a functional argument - an option that can be passed @@ -429,6 +442,18 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*APIHandler, error) { } } + // We create the cache after applying the options to make sure we use the fake clock if it was passed. + findCache, err := utils.NewFnCache(utils.FnCacheConfig{ + TTL: findEndpointCacheTTL, + Clock: h.clock, + Context: cfg.Context, + ReloadOnErr: false, + }) + if err != nil { + return nil, trace.Wrap(err, "creating /find cache") + } + h.findEndpointCache = findCache + sessionLingeringThreshold := cachedSessionLingeringThreshold if cfg.CachedSessionLingeringThreshold != nil { sessionLingeringThreshold = *cfg.CachedSessionLingeringThreshold @@ -1515,39 +1540,50 @@ func (h *Handler) ping(w http.ResponseWriter, r *http.Request, p httprouter.Para MinClientVersion: teleport.MinClientVersion, ClusterName: h.auth.clusterName, AutomaticUpgrades: pr.ServerFeatures.GetAutomaticUpgrades(), + AutoUpdate: h.automaticUpdateSettings(r.Context()), }, nil } func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { - // TODO(jent,espadolini): add a time-based cache to further reduce load on this endpoint - proxyConfig, err := h.cfg.ProxySettings.GetProxySettings(r.Context()) + // cache the generic answer to avoid doing work for each request + resp, err := utils.FnCacheGet[*webclient.PingResponse](r.Context(), h.findEndpointCache, "find", func(ctx context.Context) (*webclient.PingResponse, error) { + proxyConfig, err := h.cfg.ProxySettings.GetProxySettings(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + return &webclient.PingResponse{ + Proxy: *proxyConfig, + ServerVersion: teleport.Version, + MinClientVersion: teleport.MinClientVersion, + ClusterName: h.auth.clusterName, + AutoUpdate: h.automaticUpdateSettings(ctx), + }, nil + }) if err != nil { return nil, trace.Wrap(err) } - response := webclient.PingResponse{ - Proxy: *proxyConfig, - ServerVersion: teleport.Version, - MinClientVersion: teleport.MinClientVersion, - ClusterName: h.auth.clusterName, - } + return resp, nil +} - autoUpdateConfig, err := h.cfg.AccessPoint.GetAutoUpdateConfig(r.Context()) +// TODO: add the request as a parameter when we'll need to modulate the content based on the UUID and group +func (h *Handler) automaticUpdateSettings(ctx context.Context) webclient.AutoUpdateSettings { + autoUpdateConfig, err := h.cfg.AccessPoint.GetAutoUpdateConfig(ctx) // TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions. if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { - h.logger.ErrorContext(r.Context(), "failed to receive AutoUpdateConfig", "error", err) - } else if err == nil { - response.AutoUpdate.ToolsMode = autoUpdateConfig.GetSpec().GetTools().GetMode() + h.logger.ErrorContext(ctx, "failed to receive AutoUpdateConfig", "error", err) } - autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(r.Context()) + autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(ctx) // TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions. if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { - h.logger.ErrorContext(r.Context(), "failed to receive AutoUpdateVersion", "error", err) - } else if err == nil { - response.AutoUpdate.ToolsVersion = autoUpdateVersion.GetSpec().GetTools().GetTargetVersion() + h.logger.ErrorContext(ctx, "failed to receive AutoUpdateVersion", "error", err) } - return response, nil + return webclient.AutoUpdateSettings{ + ToolsAutoUpdate: getToolsAutoUpdate(autoUpdateConfig), + ToolsVersion: getToolsVersion(autoUpdateVersion), + } } func (h *Handler) pingWithConnector(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { @@ -4814,3 +4850,23 @@ func readEtagFromAppHash(fs http.FileSystem) (string, error) { return etag, nil } + +func getToolsAutoUpdate(config *autoupdatepb.AutoUpdateConfig) bool { + // If we can't get the AU config or if AUs are not configured, we default to "disabled". + // This ensures we fail open and don't accidentally update agents if something is going wrong. + // If we want to enable AUs by default, it would be better to create a default "autoupdate_config" resource + // than changing this logic. + if config.GetSpec().GetTools() != nil { + return config.GetSpec().GetTools().GetMode() == autoupdate.ToolsUpdateModeEnabled + } + return false +} + +func getToolsVersion(version *autoupdatepb.AutoUpdateVersion) string { + // If we can't get the AU version or tools AU version is not specified, we default to the current proxy version. + // This ensures we always advertise a version compatible with the cluster. + if version.GetSpec().GetTools() == nil { + return api.Version + } + return version.GetSpec().GetTools().GetTargetVersion() +} diff --git a/lib/web/apiserver_ping_test.go b/lib/web/apiserver_ping_test.go index 202355f517134..e6a01c365788e 100644 --- a/lib/web/apiserver_ping_test.go +++ b/lib/web/apiserver_ping_test.go @@ -30,6 +30,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/gravitational/teleport/api" "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" @@ -263,8 +264,11 @@ func TestPing_autoUpdateResources(t *testing.T) { expected webclient.AutoUpdateSettings }{ { - name: "resources not defined", - expected: webclient.AutoUpdateSettings{}, + name: "resources not defined", + expected: webclient.AutoUpdateSettings{ + ToolsVersion: api.Version, + ToolsAutoUpdate: false, + }, }, { name: "enable auto update", @@ -273,21 +277,37 @@ func TestPing_autoUpdateResources(t *testing.T) { Mode: autoupdate.ToolsUpdateModeEnabled, }, }, - expected: webclient.AutoUpdateSettings{ToolsMode: "enabled"}, - cleanup: true, + expected: webclient.AutoUpdateSettings{ + ToolsAutoUpdate: true, + ToolsVersion: api.Version, + }, + cleanup: true, + }, + { + name: "empty config and version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{}, + version: &autoupdatev1pb.AutoUpdateVersionSpec{}, + expected: webclient.AutoUpdateSettings{ + ToolsVersion: api.Version, + ToolsAutoUpdate: false, + }, + cleanup: true, }, { - name: "set auto update version", + name: "set tools auto update version", version: &autoupdatev1pb.AutoUpdateVersionSpec{ Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ TargetVersion: "1.2.3", }, }, - expected: webclient.AutoUpdateSettings{ToolsVersion: "1.2.3"}, - cleanup: true, + expected: webclient.AutoUpdateSettings{ + ToolsVersion: "1.2.3", + ToolsAutoUpdate: false, + }, + cleanup: true, }, { - name: "enable auto update and set version", + name: "enable tools auto update and set version", config: &autoupdatev1pb.AutoUpdateConfigSpec{ Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ Mode: autoupdate.ToolsUpdateModeEnabled, @@ -298,7 +318,10 @@ func TestPing_autoUpdateResources(t *testing.T) { TargetVersion: "1.2.3", }, }, - expected: webclient.AutoUpdateSettings{ToolsMode: "enabled", ToolsVersion: "1.2.3"}, + expected: webclient.AutoUpdateSettings{ + ToolsAutoUpdate: true, + ToolsVersion: "1.2.3", + }, }, { name: "modify auto update config and version", @@ -312,7 +335,10 @@ func TestPing_autoUpdateResources(t *testing.T) { TargetVersion: "3.2.1", }, }, - expected: webclient.AutoUpdateSettings{ToolsMode: "disabled", ToolsVersion: "3.2.1"}, + expected: webclient.AutoUpdateSettings{ + ToolsAutoUpdate: false, + ToolsVersion: "3.2.1", + }, }, } for _, tc := range tests { @@ -330,6 +356,11 @@ func TestPing_autoUpdateResources(t *testing.T) { require.NoError(t, err) } + // expire the fn cache to force the next answer to be fresh + for _, proxy := range env.proxies { + proxy.clock.Advance(2 * findEndpointCacheTTL) + } + resp, err := client.NewInsecureWebClient().Do(req) require.NoError(t, err) diff --git a/tool/tctl/common/tctl.go b/tool/tctl/common/tctl.go index 660ef0cc4b6fb..b933995b14f29 100644 --- a/tool/tctl/common/tctl.go +++ b/tool/tctl/common/tctl.go @@ -43,6 +43,7 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/state" "github.com/gravitational/teleport/lib/auth/storage" + "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/identityfile" libmfa "github.com/gravitational/teleport/lib/client/mfa" @@ -53,6 +54,7 @@ import ( "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/hostid" + "github.com/gravitational/teleport/lib/utils/signal" "github.com/gravitational/teleport/tool/common" ) @@ -103,8 +105,43 @@ type CLICommand interface { // "distributions" like OSS or Enterprise // // distribution: name of the Teleport distribution -func Run(commands []CLICommand) { - err := TryRun(commands, os.Args[1:]) +func Run(ctx context.Context, commands []CLICommand) { + // The user has typed a command like `tsh ssh ...` without being logged in, + // if the running binary needs to be updated, update and re-exec. + // + // If needed, download the new version of {tsh, tctl} and re-exec. Make + // sure to exit this process with the same exit code as the child process. + // + toolsDir, err := tools.Dir() + if err != nil { + utils.FatalError(err) + } + updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version) + toolsVersion, reExec, err := updater.CheckLocal() + if err != nil { + utils.FatalError(err) + } + if reExec { + ctxUpdate, cancel := signal.GetSignalHandler().NotifyContext(ctx) + defer cancel() + // Download the version of client tools required by the cluster. This + // is required if the user passed in the TELEPORT_TOOLS_VERSION + // explicitly. + err := updater.UpdateWithLock(ctxUpdate, toolsVersion) + if err != nil && !errors.Is(err, context.Canceled) { + utils.FatalError(err) + } + // Re-execute client tools with the correct version of client tools. + code, err := updater.Exec() + if err != nil && !errors.Is(err, os.ErrNotExist) { + log.Debugf("Failed to re-exec client tool: %v.", err) + os.Exit(code) + } else if err == nil { + os.Exit(code) + } + } + + err = TryRun(commands, os.Args[1:]) if err != nil { var exitError *common.ExitCodeError if errors.As(err, &exitError) { diff --git a/tool/tctl/main.go b/tool/tctl/main.go index 1eecb99209fbc..21628bd8c89e4 100644 --- a/tool/tctl/main.go +++ b/tool/tctl/main.go @@ -19,12 +19,18 @@ package main import ( + "context" + + "github.com/gravitational/teleport/lib/utils/signal" "github.com/gravitational/teleport/tool/tctl/common" ) func main() { + ctx, cancel := signal.GetSignalHandler().NotifyContext(context.Background()) + defer cancel() + // aggregate common and oss-specific command variants commands := common.Commands() commands = append(commands, common.OSSCommands()...) - common.Run(commands) + common.Run(ctx, commands) } diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index cbe2a3ef6ea20..f864351d8bdff 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -41,7 +41,6 @@ import ( "strconv" "strings" "sync" - "syscall" "time" "github.com/alecthomas/kingpin/v2" @@ -74,6 +73,7 @@ import ( "github.com/gravitational/teleport/lib/asciitable" "github.com/gravitational/teleport/lib/auth/authclient" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/benchmark" benchmarkdb "github.com/gravitational/teleport/lib/benchmark/db" "github.com/gravitational/teleport/lib/client" @@ -94,6 +94,7 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/diagnostics/latency" "github.com/gravitational/teleport/lib/utils/mlock" + stacksignal "github.com/gravitational/teleport/lib/utils/signal" "github.com/gravitational/teleport/tool/common" "github.com/gravitational/teleport/tool/common/fido2" "github.com/gravitational/teleport/tool/common/touchid" @@ -605,7 +606,7 @@ func Main() { cmdLineOrig := os.Args[1:] var cmdLine []string - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) + ctx, cancel := stacksignal.GetSignalHandler().NotifyContext(context.Background()) defer cancel() // lets see: if the executable name is 'ssh' or 'scp' we convert @@ -703,6 +704,38 @@ func initLogger(cf *CLIConf) { // // DO NOT RUN TESTS that call Run() in parallel (unless you taken precautions). func Run(ctx context.Context, args []string, opts ...CliOption) error { + // At process startup, check if a version has already been downloaded to + // $TELEPORT_HOME/bin or if the user has set the TELEPORT_TOOLS_VERSION + // environment variable. If so, re-exec that version of {tsh, tctl}. + toolsDir, err := tools.Dir() + if err != nil { + return trace.Wrap(err) + } + updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version) + toolsVersion, reExec, err := updater.CheckLocal() + if err != nil { + return trace.Wrap(err) + } + if reExec { + ctxUpdate, cancel := stacksignal.GetSignalHandler().NotifyContext(ctx) + defer cancel() + // Download the version of client tools required by the cluster. This + // is required if the user passed in the TELEPORT_TOOLS_VERSION + // explicitly. + err := updater.UpdateWithLock(ctxUpdate, toolsVersion) + if err != nil && !errors.Is(err, context.Canceled) { + return trace.Wrap(err) + } + // Re-execute client tools with the correct version of client tools. + code, err := updater.Exec() + if err != nil && !errors.Is(err, os.ErrNotExist) { + log.Debugf("Failed to re-exec client tool: %v.", err) + os.Exit(code) + } else if err == nil { + os.Exit(code) + } + } + cf := CLIConf{ Context: ctx, TracingProvider: tracing.NoopProvider(), @@ -1227,8 +1260,6 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { bench.Hidden() } - var err error - cf.executablePath, err = os.Executable() if err != nil { return trace.Wrap(err) @@ -1848,6 +1879,14 @@ func onLogin(cf *CLIConf) error { } tc.HomePath = cf.HomePath + // The user is not logged in and has typed in `tsh --proxy=... login`, if + // the running binary needs to be updated, update and re-exec. + if profile == nil { + if err := updateAndRun(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + return trace.Wrap(err) + } + } + // client is already logged in and profile is not expired if profile != nil && !profile.IsExpired(clockwork.NewRealClock()) { switch { @@ -1858,6 +1897,13 @@ func onLogin(cf *CLIConf) error { // current status case cf.Proxy == "" && cf.SiteName == "" && cf.DesiredRoles == "" && cf.RequestID == "" && cf.IdentityFileOut == "" || host(cf.Proxy) == host(profile.ProxyURL.Host) && cf.SiteName == profile.Cluster && cf.DesiredRoles == "" && cf.RequestID == "": + + // The user has typed `tsh login`, if the running binary needs to + // be updated, update and re-exec. + if err := updateAndRun(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + return trace.Wrap(err) + } + _, err := tc.PingAndShowMOTD(cf.Context) if err != nil { return trace.Wrap(err) @@ -1871,6 +1917,13 @@ func onLogin(cf *CLIConf) error { // if the proxy names match but nothing else is specified; show motd and update active profile and kube configs case host(cf.Proxy) == host(profile.ProxyURL.Host) && cf.SiteName == "" && cf.DesiredRoles == "" && cf.RequestID == "" && cf.IdentityFileOut == "": + + // The user has typed `tsh login`, if the running binary needs to + // be updated, update and re-exec. + if err := updateAndRun(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + return trace.Wrap(err) + } + _, err := tc.PingAndShowMOTD(cf.Context) if err != nil { return trace.Wrap(err) @@ -1941,7 +1994,11 @@ func onLogin(cf *CLIConf) error { // otherwise just pass through to standard login default: - + // The user is logged in and has typed in `tsh --proxy=... login`, if + // the running binary needs to be updated, update and re-exec. + if err := updateAndRun(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + return trace.Wrap(err) + } } } @@ -5470,6 +5527,43 @@ const ( "https://goteleport.com/docs/access-controls/guides/headless/#troubleshooting" ) +func updateAndRun(ctx context.Context, proxy string, insecure bool) error { + // The user has typed a command like `tsh ssh ...` without being logged in, + // if the running binary needs to be updated, update and re-exec. + // + // If needed, download the new version of {tsh, tctl} and re-exec. Make + // sure to exit this process with the same exit code as the child process. + // + toolsDir, err := tools.Dir() + if err != nil { + return trace.Wrap(err) + } + updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version) + toolsVersion, reExec, err := updater.CheckRemote(ctx, proxy, insecure) + if err != nil { + return trace.Wrap(err) + } + if reExec { + ctxUpdate, cancel := stacksignal.GetSignalHandler().NotifyContext(context.Background()) + defer cancel() + // Download the version of client tools required by the cluster. + err := updater.UpdateWithLock(ctxUpdate, toolsVersion) + if err != nil && !errors.Is(err, context.Canceled) { + return trace.Wrap(err) + } + // Re-execute client tools with the correct version of client tools. + code, err := updater.Exec() + if err != nil && !errors.Is(err, os.ErrNotExist) { + log.Debugf("Failed to re-exec client tool: %v.", err) + os.Exit(code) + } else if err == nil { + os.Exit(code) + } + } + + return nil +} + // Lock the process memory to prevent rsa keys and certificates in memory from being exposed in a swap. func tryLockMemory(cf *CLIConf) error { if cf.MlockMode == mlockModeAuto { From b0c1599b0a470a0d1033efc0f1034683cfd1d0cd Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Thu, 31 Oct 2024 14:07:42 -0400 Subject: [PATCH 6/9] Add audit logs for auto update resources (#48218) --- .../teleport/legacy/types/events/events.proto | 42 + api/types/events/events.go | 24 + api/types/events/events.pb.go | 2250 ++++++++++------- api/types/events/oneof.go | 25 + lib/auth/autoupdate/autoupdatev1/service.go | 199 +- .../autoupdate/autoupdatev1/service_test.go | 113 +- lib/auth/grpcserver.go | 1 + lib/autoupdate/tools/utils.go | 2 +- lib/events/api.go | 14 + lib/events/codes.go | 14 + lib/events/dynamic.go | 14 + lib/events/events_test.go | 6 + lib/web/apiserver.go | 7 +- 13 files changed, 1707 insertions(+), 1004 deletions(-) diff --git a/api/proto/teleport/legacy/types/events/events.proto b/api/proto/teleport/legacy/types/events/events.proto index 7e11d0eec885b..899d329dd2e6d 100644 --- a/api/proto/teleport/legacy/types/events/events.proto +++ b/api/proto/teleport/legacy/types/events/events.proto @@ -6777,6 +6777,13 @@ message AutoUpdateConfigCreate { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // Status indicates whether the creation was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateConfigUpdate is emitted when an auto update config is updated. @@ -6808,6 +6815,13 @@ message AutoUpdateConfigUpdate { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateConfigDelete is emitted when an auto update config is deleted. @@ -6839,6 +6853,13 @@ message AutoUpdateConfigDelete { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // Status indicates whether the deletion was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateVersionCreate is emitted when an auto update version is created. @@ -6870,6 +6891,13 @@ message AutoUpdateVersionCreate { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // Status indicates whether the creation was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateVersionUpdate is emitted when an auto update version is updated. @@ -6901,6 +6929,13 @@ message AutoUpdateVersionUpdate { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateVersionDelete is emitted when an auto update version is deleted. @@ -6932,6 +6967,13 @@ message AutoUpdateVersionDelete { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // Status indicates whether the deletion was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // CrownJewelCreate is emitted when a Access Graph CrownJewel is created. diff --git a/api/types/events/events.go b/api/types/events/events.go index 77dffca80c81d..9aef279423e84 100644 --- a/api/types/events/events.go +++ b/api/types/events/events.go @@ -2255,3 +2255,27 @@ func (m *CrownJewelUpdate) TrimToMaxSize(_ int) AuditEvent { func (m *CrownJewelDelete) TrimToMaxSize(_ int) AuditEvent { return m } + +func (m *AutoUpdateConfigCreate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateConfigUpdate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateConfigDelete) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateVersionCreate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateVersionUpdate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateVersionDelete) TrimToMaxSize(_ int) AuditEvent { + return m +} diff --git a/api/types/events/events.pb.go b/api/types/events/events.pb.go index 8374abcbfad25..d5464b7440a9b 100644 --- a/api/types/events/events.pb.go +++ b/api/types/events/events.pb.go @@ -13697,7 +13697,9 @@ type AutoUpdateConfigCreate struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the creation was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -13745,7 +13747,9 @@ type AutoUpdateConfigUpdate struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // ResourceMetadata is a common resource event metadata + ResourceMetadata `protobuf:"bytes,5,opt,name=Resource,proto3,embedded=Resource" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -13793,7 +13797,9 @@ type AutoUpdateConfigDelete struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the deletion was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -13841,7 +13847,9 @@ type AutoUpdateVersionCreate struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the creation was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -13889,7 +13897,9 @@ type AutoUpdateVersionUpdate struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // ResourceMetadata is a common resource event metadata + ResourceMetadata `protobuf:"bytes,5,opt,name=Resource,proto3,embedded=Resource" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -13937,7 +13947,9 @@ type AutoUpdateVersionDelete struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the deletion was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -14385,999 +14397,999 @@ func init() { } var fileDescriptor_007ba1c3d6266d56 = []byte{ - // 15859 bytes of a gzipped FileDescriptorProto + // 15863 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x74, 0x1c, 0x47, 0x76, 0x18, 0x8c, 0x79, 0x60, 0x00, 0x14, 0x1e, 0x04, 0x8a, 0x14, 0xd9, 0xa4, 0x48, 0x8e, 0xd4, 0x92, 0xb8, 0xa4, 0x56, 0x22, 0x25, 0x8a, 0x92, 0x56, 0xaf, 0x95, 0x06, 0x18, 0x80, 0x18, 0x11, - 0x8f, 0x51, 0x0f, 0x48, 0xae, 0xf6, 0x35, 0x6e, 0x4c, 0x17, 0x80, 0x16, 0x67, 0xba, 0x67, 0xbb, + 0x8f, 0x51, 0x0f, 0x48, 0xae, 0xf6, 0x35, 0x6e, 0x4c, 0x17, 0x80, 0x16, 0x66, 0xba, 0x67, 0xbb, 0x7b, 0x08, 0x42, 0xdf, 0xcb, 0xeb, 0xcf, 0x8f, 0x5d, 0x7b, 0x77, 0xbd, 0xdf, 0xfa, 0xf3, 0xdb, 0x49, 0xd6, 0x76, 0x9c, 0xd8, 0x8e, 0xed, 0xb5, 0x1d, 0x9f, 0xb5, 0xd7, 0xce, 0x9e, 0xd8, 0xd9, 0xe4, 0x44, 0xf6, 0x26, 0x3e, 0xb6, 0x93, 0xf8, 0xf8, 0x24, 0x0e, 0xd6, 0xd9, 0xc4, 0xf9, 0x81, 0x13, 0x9f, 0xe3, 0x24, 0x7b, 0xe2, 0x8d, 0xe3, 0xe4, 0xe4, 0xd4, 0xad, 0xea, 0xee, 0xaa, 0x7e, - 0x0c, 0x9e, 0x32, 0x04, 0x01, 0x7f, 0x48, 0xcc, 0xbd, 0xb7, 0x6e, 0x55, 0xdf, 0xba, 0x55, 0x75, + 0x0c, 0x9e, 0x32, 0x04, 0x11, 0x7f, 0x48, 0xcc, 0xbd, 0xb7, 0x6e, 0x55, 0xdf, 0xba, 0x55, 0x75, 0xab, 0xea, 0xd6, 0xbd, 0xe8, 0x92, 0x47, 0x9a, 0xa4, 0x6d, 0x3b, 0xde, 0x95, 0x26, 0x59, 0xd6, - 0x1b, 0x6b, 0x57, 0xbc, 0xb5, 0x36, 0x71, 0xaf, 0x90, 0xbb, 0xc4, 0xf2, 0xfc, 0xff, 0x2e, 0xb7, + 0x1b, 0xeb, 0x57, 0xbc, 0xf5, 0x36, 0x71, 0xaf, 0x90, 0x3b, 0xc4, 0xf2, 0xfc, 0xff, 0x2e, 0xb7, 0x1d, 0xdb, 0xb3, 0x71, 0x81, 0xfd, 0x3a, 0x73, 0x62, 0xd9, 0x5e, 0xb6, 0x01, 0x74, 0x85, 0xfe, 0xc5, 0xb0, 0x67, 0xce, 0x2e, 0xdb, 0xf6, 0x72, 0x93, 0x5c, 0x81, 0x5f, 0x8b, 0x9d, 0xa5, 0x2b, 0xae, 0xe7, 0x74, 0x1a, 0x1e, 0xc7, 0x16, 0xa3, 0x58, 0xcf, 0x6c, 0x11, 0xd7, 0xd3, 0x5b, 0x6d, - 0x4e, 0x70, 0x3e, 0x4a, 0xb0, 0xea, 0xe8, 0xed, 0x36, 0x71, 0x78, 0xe5, 0x67, 0x1e, 0x4c, 0x6e, + 0x4e, 0x70, 0x3e, 0x4a, 0xb0, 0xe6, 0xe8, 0xed, 0x36, 0x71, 0x78, 0xe5, 0x67, 0x1e, 0x4c, 0x6e, 0x27, 0xfc, 0xcb, 0x49, 0x1e, 0x4f, 0x26, 0xf1, 0x19, 0x45, 0x38, 0xaa, 0x9f, 0xcb, 0xa2, 0xfe, - 0x59, 0xe2, 0xe9, 0x86, 0xee, 0xe9, 0xf8, 0x2c, 0xea, 0xad, 0x58, 0x06, 0xb9, 0xa7, 0x64, 0x1e, - 0xc8, 0x5c, 0xcc, 0x8d, 0x17, 0x36, 0xd6, 0x8b, 0x59, 0x62, 0x6a, 0x0c, 0x88, 0xcf, 0xa1, 0xfc, - 0xc2, 0x5a, 0x9b, 0x28, 0xd9, 0x07, 0x32, 0x17, 0x07, 0xc6, 0x07, 0x36, 0xd6, 0x8b, 0xbd, 0x20, - 0x0b, 0x0d, 0xc0, 0xf8, 0x41, 0x94, 0xad, 0x94, 0x95, 0x1c, 0x20, 0xc7, 0x36, 0xd6, 0x8b, 0xc3, - 0x1d, 0xd3, 0x78, 0xcc, 0x6e, 0x99, 0x1e, 0x69, 0xb5, 0xbd, 0x35, 0x2d, 0x5b, 0x29, 0xe3, 0x0b, - 0x28, 0x3f, 0x61, 0x1b, 0x44, 0xc9, 0x03, 0x11, 0xde, 0x58, 0x2f, 0x8e, 0x34, 0x6c, 0x83, 0x08, + 0x59, 0xe2, 0xe9, 0x86, 0xee, 0xe9, 0xf8, 0x2c, 0xea, 0xad, 0x58, 0x06, 0xb9, 0xab, 0x64, 0x1e, + 0xc8, 0x5c, 0xcc, 0x8d, 0x17, 0x36, 0x37, 0x8a, 0x59, 0x62, 0x6a, 0x0c, 0x88, 0xcf, 0xa1, 0xfc, + 0xc2, 0x7a, 0x9b, 0x28, 0xd9, 0x07, 0x32, 0x17, 0x07, 0xc6, 0x07, 0x36, 0x37, 0x8a, 0xbd, 0x20, + 0x0b, 0x0d, 0xc0, 0xf8, 0x41, 0x94, 0xad, 0x94, 0x95, 0x1c, 0x20, 0xc7, 0x36, 0x37, 0x8a, 0xc3, + 0x1d, 0xd3, 0x78, 0xcc, 0x6e, 0x99, 0x1e, 0x69, 0xb5, 0xbd, 0x75, 0x2d, 0x5b, 0x29, 0xe3, 0x0b, + 0x28, 0x3f, 0x61, 0x1b, 0x44, 0xc9, 0x03, 0x11, 0xde, 0xdc, 0x28, 0x8e, 0x34, 0x6c, 0x83, 0x08, 0x54, 0x80, 0xc7, 0xaf, 0xa0, 0xfc, 0x82, 0xd9, 0x22, 0x4a, 0xef, 0x03, 0x99, 0x8b, 0x83, 0x57, - 0xcf, 0x5c, 0x66, 0x52, 0xb9, 0xec, 0x4b, 0xe5, 0xf2, 0x82, 0x2f, 0xb6, 0xf1, 0xd1, 0xb7, 0xd6, - 0x8b, 0x3d, 0x1b, 0xeb, 0xc5, 0x3c, 0x95, 0xe4, 0x67, 0xbf, 0x56, 0xcc, 0x68, 0x50, 0x12, 0xbf, + 0xcf, 0x5c, 0x66, 0x52, 0xb9, 0xec, 0x4b, 0xe5, 0xf2, 0x82, 0x2f, 0xb6, 0xf1, 0xd1, 0xb7, 0x36, + 0x8a, 0x3d, 0x9b, 0x1b, 0xc5, 0x3c, 0x95, 0xe4, 0x67, 0xbf, 0x56, 0xcc, 0x68, 0x50, 0x12, 0xbf, 0x88, 0x06, 0x27, 0x9a, 0x1d, 0xd7, 0x23, 0xce, 0x9c, 0xde, 0x22, 0x4a, 0x01, 0x2a, 0x3c, 0xb3, - 0xb1, 0x5e, 0x3c, 0xd9, 0x60, 0xe0, 0xba, 0xa5, 0xb7, 0xc4, 0x8a, 0x45, 0x72, 0xf5, 0xd7, 0x32, + 0xb9, 0x51, 0x3c, 0xd9, 0x60, 0xe0, 0xba, 0xa5, 0xb7, 0xc4, 0x8a, 0x45, 0x72, 0xf5, 0xd7, 0x32, 0xe8, 0x58, 0x8d, 0xb8, 0xae, 0x69, 0x5b, 0x81, 0x6c, 0x1e, 0x41, 0x03, 0x1c, 0x54, 0x29, 0x83, - 0x7c, 0x06, 0xc6, 0xfb, 0x36, 0xd6, 0x8b, 0x39, 0xd7, 0x34, 0xb4, 0x10, 0x83, 0x9f, 0x40, 0x7d, - 0xb7, 0x4d, 0x6f, 0x65, 0x76, 0xaa, 0xc4, 0xe5, 0x74, 0x72, 0x63, 0xbd, 0x88, 0x57, 0x4d, 0x6f, - 0xa5, 0xde, 0x5a, 0xd2, 0x85, 0x0a, 0x7d, 0x32, 0x3c, 0x83, 0x46, 0xab, 0x8e, 0x79, 0x57, 0xf7, - 0xc8, 0x0d, 0xb2, 0x56, 0xb5, 0x9b, 0x66, 0x63, 0x8d, 0x4b, 0xf1, 0x81, 0x8d, 0xf5, 0xe2, 0xd9, - 0x36, 0xc3, 0xd5, 0xef, 0x90, 0xb5, 0x7a, 0x1b, 0xb0, 0x02, 0x93, 0x58, 0x49, 0xf5, 0x2b, 0xbd, - 0x68, 0xe8, 0xa6, 0x4b, 0x9c, 0xa0, 0xdd, 0x17, 0x50, 0x9e, 0xfe, 0xe6, 0x4d, 0x06, 0x99, 0x77, - 0x5c, 0xe2, 0x88, 0x32, 0xa7, 0x78, 0x7c, 0x09, 0xf5, 0xce, 0xd8, 0xcb, 0xa6, 0xc5, 0x9b, 0x7d, - 0x7c, 0x63, 0xbd, 0x78, 0xac, 0x49, 0x01, 0x02, 0x25, 0xa3, 0xc0, 0xef, 0x47, 0x43, 0x95, 0x16, - 0xd5, 0x21, 0xdb, 0xd2, 0x3d, 0xdb, 0xe1, 0xad, 0x05, 0xe9, 0x9a, 0x02, 0x5c, 0x28, 0x28, 0xd1, - 0xe3, 0xe7, 0x11, 0x2a, 0xdd, 0xae, 0x69, 0x76, 0x93, 0x94, 0xb4, 0x39, 0xae, 0x0c, 0x50, 0x5a, - 0x5f, 0x75, 0xeb, 0x8e, 0xdd, 0x24, 0x75, 0xdd, 0x11, 0xab, 0x15, 0xa8, 0xf1, 0x24, 0x1a, 0x29, - 0x35, 0x1a, 0xc4, 0x75, 0x35, 0xf2, 0xb1, 0x0e, 0x71, 0x3d, 0x57, 0xe9, 0x7d, 0x20, 0x77, 0x71, - 0x60, 0xfc, 0xdc, 0xc6, 0x7a, 0xf1, 0xb4, 0x0e, 0x98, 0xba, 0xc3, 0x51, 0x02, 0x8b, 0x48, 0x21, - 0x3c, 0x8e, 0x86, 0x4b, 0x6f, 0x76, 0x1c, 0x52, 0x31, 0x88, 0xe5, 0x99, 0xde, 0x1a, 0xd7, 0x90, - 0xb3, 0x1b, 0xeb, 0x45, 0x45, 0xa7, 0x88, 0xba, 0xc9, 0x31, 0x02, 0x13, 0xb9, 0x08, 0x9e, 0x47, - 0x63, 0xd7, 0x27, 0xaa, 0x35, 0xe2, 0xdc, 0x35, 0x1b, 0xa4, 0xd4, 0x68, 0xd8, 0x1d, 0xcb, 0x53, - 0xfa, 0x80, 0xcf, 0x83, 0x1b, 0xeb, 0xc5, 0x73, 0xcb, 0x8d, 0x76, 0xdd, 0x65, 0xd8, 0xba, 0xce, - 0xd0, 0x02, 0xb3, 0x78, 0x59, 0xfc, 0x41, 0x34, 0xbc, 0xe0, 0x50, 0x2d, 0x34, 0xca, 0x84, 0xc2, - 0x95, 0x7e, 0xd0, 0xff, 0x93, 0x97, 0xf9, 0x04, 0xc4, 0xa0, 0x7e, 0xcf, 0xb2, 0xc6, 0x7a, 0xac, - 0x40, 0xdd, 0x00, 0x9c, 0xd8, 0x58, 0x89, 0x15, 0x26, 0x48, 0xa1, 0x1f, 0x6f, 0x3a, 0xc4, 0x88, - 0x69, 0xdb, 0x00, 0xb4, 0xf9, 0xd2, 0xc6, 0x7a, 0xf1, 0x11, 0x87, 0xd3, 0xd4, 0xbb, 0xaa, 0x5d, - 0x2a, 0x2b, 0x3c, 0x89, 0xfa, 0xa9, 0x36, 0xdd, 0x30, 0x2d, 0x43, 0x41, 0x0f, 0x64, 0x2e, 0x8e, - 0x5c, 0x1d, 0xf5, 0x5b, 0xef, 0xc3, 0xc7, 0x4f, 0x6d, 0xac, 0x17, 0x8f, 0x53, 0x1d, 0xac, 0xdf, - 0x31, 0x2d, 0x71, 0x8a, 0x08, 0x8a, 0xaa, 0x7f, 0x91, 0x47, 0x23, 0x54, 0x38, 0x82, 0x1e, 0x97, - 0xe8, 0x90, 0xa4, 0x10, 0x3a, 0x42, 0xdd, 0xb6, 0xde, 0x20, 0x5c, 0xa5, 0x81, 0x9d, 0xe5, 0x03, - 0x05, 0x76, 0x51, 0x7a, 0x7c, 0x09, 0xf5, 0x33, 0x50, 0xa5, 0xcc, 0xb5, 0x7c, 0x78, 0x63, 0xbd, - 0x38, 0xe0, 0x02, 0xac, 0x6e, 0x1a, 0x5a, 0x80, 0xa6, 0x6a, 0xc6, 0xfe, 0x9e, 0xb6, 0x5d, 0x8f, - 0x32, 0xe7, 0x4a, 0x0e, 0x6a, 0xc6, 0x0b, 0xac, 0x70, 0x94, 0xa8, 0x66, 0x72, 0x21, 0xfc, 0x1c, - 0x42, 0x0c, 0x52, 0x32, 0x0c, 0x87, 0x6b, 0xfa, 0xe9, 0x8d, 0xf5, 0xe2, 0x7d, 0x9c, 0x85, 0x6e, - 0x18, 0xe2, 0x30, 0x11, 0x88, 0x71, 0x0b, 0x0d, 0xb1, 0x5f, 0x33, 0xfa, 0x22, 0x69, 0x32, 0x35, - 0x1f, 0xbc, 0x7a, 0xd1, 0x97, 0xa6, 0x2c, 0x9d, 0xcb, 0x22, 0xe9, 0xa4, 0xe5, 0x39, 0x6b, 0xe3, - 0x45, 0x3e, 0x33, 0x9e, 0xe2, 0x55, 0x35, 0x01, 0x27, 0x8e, 0x49, 0xb1, 0x0c, 0x9d, 0x30, 0xa7, - 0x6c, 0x67, 0x55, 0x77, 0x0c, 0x62, 0x8c, 0xaf, 0x89, 0x13, 0xe6, 0x92, 0x0f, 0xae, 0x2f, 0x8a, - 0x3a, 0x20, 0x92, 0xe3, 0x09, 0x34, 0xcc, 0xb8, 0xd5, 0x3a, 0x8b, 0xd0, 0xf7, 0x7d, 0x31, 0x69, - 0xb9, 0x9d, 0xc5, 0x68, 0x7f, 0xcb, 0x65, 0xe8, 0x98, 0x64, 0x80, 0x5b, 0xc4, 0xa1, 0xb3, 0x29, - 0xa8, 0x3f, 0x1f, 0x93, 0x9c, 0xc9, 0x5d, 0x86, 0x89, 0xf3, 0xe0, 0x45, 0xce, 0xbc, 0x8c, 0xc6, - 0x62, 0xa2, 0xc0, 0xa3, 0x28, 0x77, 0x87, 0xac, 0x31, 0x75, 0xd1, 0xe8, 0x9f, 0xf8, 0x04, 0xea, - 0xbd, 0xab, 0x37, 0x3b, 0x7c, 0x2d, 0xd3, 0xd8, 0x8f, 0xe7, 0xb3, 0xef, 0xcb, 0xd0, 0xa9, 0x1f, - 0x4f, 0xd8, 0x96, 0x45, 0x1a, 0x9e, 0x38, 0xfb, 0x3f, 0x83, 0x06, 0x66, 0xec, 0x86, 0xde, 0x84, - 0x7e, 0x64, 0x7a, 0xa7, 0x6c, 0xac, 0x17, 0x4f, 0xd0, 0x0e, 0xbc, 0xdc, 0xa4, 0x18, 0xa1, 0x4d, - 0x21, 0x29, 0x55, 0x00, 0x8d, 0xb4, 0x6c, 0x8f, 0x40, 0xc1, 0x6c, 0xa8, 0x00, 0x50, 0xd0, 0x01, - 0x94, 0xa8, 0x00, 0x21, 0x31, 0xbe, 0x82, 0xfa, 0xab, 0x74, 0xc1, 0x6b, 0xd8, 0x4d, 0xae, 0x7c, - 0x30, 0x27, 0xc3, 0x22, 0x28, 0x0e, 0x1a, 0x9f, 0x48, 0x9d, 0x46, 0x23, 0x13, 0x4d, 0x93, 0x58, - 0x9e, 0xd8, 0x6a, 0x3a, 0xa4, 0x4a, 0xcb, 0xc4, 0xf2, 0xc4, 0x56, 0xc3, 0xe0, 0xd3, 0x29, 0x54, - 0x6c, 0x75, 0x40, 0xaa, 0xfe, 0x6e, 0x0e, 0x9d, 0xbe, 0xd1, 0x59, 0x24, 0x8e, 0x45, 0x3c, 0xe2, - 0xf2, 0x95, 0x31, 0xe0, 0x3a, 0x87, 0xc6, 0x62, 0x48, 0xce, 0x1d, 0x56, 0xac, 0x3b, 0x01, 0xb2, - 0xce, 0x17, 0x5b, 0x71, 0xda, 0x8b, 0x15, 0xc5, 0xd3, 0xe8, 0x58, 0x08, 0xa4, 0x8d, 0x70, 0x95, - 0x2c, 0xcc, 0xe9, 0xe7, 0x37, 0xd6, 0x8b, 0x67, 0x04, 0x6e, 0xb4, 0xd9, 0xa2, 0x06, 0x47, 0x8b, - 0xe1, 0x1b, 0x68, 0x34, 0x04, 0x5d, 0x77, 0xec, 0x4e, 0xdb, 0x55, 0x72, 0xc0, 0xaa, 0xb8, 0xb1, - 0x5e, 0xbc, 0x5f, 0x60, 0xb5, 0x0c, 0x48, 0x71, 0x25, 0x8d, 0x16, 0xc4, 0xdf, 0x9e, 0x11, 0xb9, - 0xf1, 0x51, 0x98, 0x87, 0x51, 0xf8, 0xac, 0x3f, 0x0a, 0x53, 0x85, 0x74, 0x39, 0x5a, 0x92, 0x0f, - 0xca, 0x48, 0x33, 0x62, 0x83, 0x32, 0x56, 0xe3, 0x99, 0x09, 0x74, 0x5f, 0x22, 0xaf, 0x6d, 0x69, - 0xf5, 0x9f, 0xe6, 0x44, 0x2e, 0x55, 0xdb, 0x08, 0x3a, 0x73, 0x5e, 0xec, 0xcc, 0xaa, 0x6d, 0x80, - 0xb9, 0x94, 0x09, 0x17, 0x31, 0xa1, 0xb1, 0x6d, 0xdb, 0x88, 0x5a, 0x4d, 0xf1, 0xb2, 0xf8, 0xa3, - 0xe8, 0x64, 0x0c, 0xc8, 0xa6, 0x6b, 0xa6, 0xfd, 0x17, 0x36, 0xd6, 0x8b, 0x6a, 0x02, 0xd7, 0xe8, - 0xec, 0x9d, 0xc2, 0x05, 0xeb, 0xe8, 0x94, 0x20, 0x75, 0xdb, 0xf2, 0x74, 0xd3, 0xe2, 0x56, 0x1e, - 0x1b, 0x25, 0xef, 0xd9, 0x58, 0x2f, 0x3e, 0x24, 0xea, 0xa0, 0x4f, 0x13, 0x6d, 0x7c, 0x1a, 0x1f, - 0x6c, 0x20, 0x25, 0x01, 0x55, 0x69, 0xe9, 0xcb, 0xbe, 0xe9, 0x7a, 0x71, 0x63, 0xbd, 0xf8, 0x70, - 0x62, 0x1d, 0x26, 0xa5, 0x12, 0x97, 0xca, 0x34, 0x4e, 0x58, 0x43, 0x38, 0xc4, 0xcd, 0xd9, 0x06, - 0x81, 0x6f, 0xe8, 0x05, 0xfe, 0xea, 0xc6, 0x7a, 0xf1, 0xbc, 0xc0, 0xdf, 0xb2, 0x0d, 0x12, 0x6d, - 0x7e, 0x42, 0x69, 0xf5, 0xd7, 0x72, 0xe8, 0x7c, 0xad, 0x34, 0x3b, 0x53, 0x31, 0x7c, 0xdb, 0xa2, - 0xea, 0xd8, 0x77, 0x4d, 0x43, 0x18, 0xbd, 0x8b, 0xe8, 0x54, 0x04, 0x35, 0x09, 0xe6, 0x4c, 0x60, - 0xd5, 0xc2, 0xb7, 0xf9, 0x76, 0x4b, 0x9b, 0xd3, 0xd4, 0x99, 0xcd, 0x53, 0x97, 0x4c, 0xfa, 0x34, - 0x46, 0xb4, 0x8f, 0x22, 0xa8, 0xda, 0x8a, 0xed, 0x78, 0x8d, 0x8e, 0xc7, 0x95, 0x00, 0xfa, 0x28, - 0x56, 0x87, 0xcb, 0x89, 0xba, 0x54, 0xe1, 0xf3, 0xc1, 0x9f, 0xcc, 0xa0, 0xd1, 0x92, 0xe7, 0x39, - 0xe6, 0x62, 0xc7, 0x23, 0xb3, 0x7a, 0xbb, 0x6d, 0x5a, 0xcb, 0x30, 0xd6, 0x07, 0xaf, 0xbe, 0x18, - 0xac, 0x91, 0x5d, 0x25, 0x71, 0x39, 0x5a, 0x5c, 0x18, 0xa2, 0xba, 0x8f, 0xaa, 0xb7, 0x18, 0x4e, - 0x1c, 0xa2, 0xd1, 0x72, 0x74, 0x88, 0x26, 0xf2, 0xda, 0xd6, 0x10, 0xfd, 0x5c, 0x0e, 0x9d, 0x9d, - 0xbf, 0xe3, 0xe9, 0x1a, 0x71, 0xed, 0x8e, 0xd3, 0x20, 0xee, 0xcd, 0xb6, 0xa1, 0x7b, 0x24, 0x1c, - 0xa9, 0x45, 0xd4, 0x5b, 0x32, 0x0c, 0x62, 0x00, 0xbb, 0x5e, 0xb6, 0xff, 0xd2, 0x29, 0x40, 0x63, - 0x70, 0xfc, 0x08, 0xea, 0xe3, 0x65, 0x80, 0x7b, 0xef, 0xf8, 0xe0, 0xc6, 0x7a, 0xb1, 0xaf, 0xc3, - 0x40, 0x9a, 0x8f, 0xa3, 0x64, 0x65, 0xd2, 0x24, 0x94, 0x2c, 0x17, 0x92, 0x19, 0x0c, 0xa4, 0xf9, - 0x38, 0xfc, 0x1a, 0x1a, 0x01, 0xb6, 0x41, 0x7b, 0xf8, 0xdc, 0x77, 0xc2, 0x97, 0xae, 0xd8, 0x58, - 0xb6, 0x34, 0x41, 0x6b, 0xea, 0x8e, 0x5f, 0x40, 0x8b, 0x30, 0xc0, 0xb7, 0xd1, 0x28, 0x6f, 0x44, - 0xc8, 0xb4, 0xb7, 0x0b, 0xd3, 0xfb, 0x36, 0xd6, 0x8b, 0x63, 0xbc, 0xfd, 0x02, 0xdb, 0x18, 0x13, - 0xca, 0x98, 0x37, 0x3b, 0x64, 0x5c, 0xd8, 0x8c, 0x31, 0xff, 0x62, 0x91, 0x71, 0x94, 0x89, 0xfa, - 0x3a, 0x1a, 0x12, 0x0b, 0xe2, 0x93, 0xb0, 0xc7, 0x65, 0xe3, 0x04, 0x76, 0xc7, 0xa6, 0x01, 0x1b, - 0xdb, 0x27, 0xd1, 0x60, 0x99, 0xb8, 0x0d, 0xc7, 0x6c, 0x53, 0xab, 0x81, 0x2b, 0xf9, 0xb1, 0x8d, - 0xf5, 0xe2, 0xa0, 0x11, 0x82, 0x35, 0x91, 0x46, 0xfd, 0x6f, 0x19, 0x74, 0x92, 0xf2, 0x2e, 0xb9, - 0xae, 0xb9, 0x6c, 0xb5, 0xc4, 0x65, 0xfb, 0x31, 0x54, 0xa8, 0x41, 0x7d, 0xbc, 0xa6, 0x13, 0x1b, - 0xeb, 0xc5, 0x51, 0xd6, 0x02, 0x41, 0x0f, 0x39, 0x4d, 0xb0, 0xc1, 0xcb, 0x6e, 0xb2, 0xc1, 0xa3, - 0x26, 0xad, 0xa7, 0x3b, 0x9e, 0x69, 0x2d, 0xd7, 0x3c, 0xdd, 0xeb, 0xb8, 0x92, 0x49, 0xcb, 0x31, - 0x75, 0x17, 0x50, 0x92, 0x49, 0x2b, 0x15, 0xc2, 0x2f, 0xa3, 0xa1, 0x49, 0xcb, 0x08, 0x99, 0xb0, - 0x09, 0xf1, 0x7e, 0x6a, 0x69, 0x12, 0x80, 0xc7, 0x59, 0x48, 0x05, 0xd4, 0x9f, 0xcf, 0x20, 0x85, - 0xed, 0xc6, 0x66, 0x4c, 0xd7, 0x9b, 0x25, 0xad, 0x45, 0x61, 0x76, 0x9a, 0xf2, 0xb7, 0x77, 0x14, - 0x27, 0xac, 0x45, 0x60, 0x0a, 0xf0, 0xed, 0x5d, 0xd3, 0x74, 0xbd, 0xe8, 0x64, 0x18, 0x29, 0x85, - 0x2b, 0xa8, 0x8f, 0x71, 0x66, 0xb6, 0xc4, 0xe0, 0x55, 0xc5, 0x57, 0x84, 0x68, 0xd5, 0x4c, 0x19, - 0x5a, 0x8c, 0x58, 0xdc, 0x9f, 0xf3, 0xf2, 0xea, 0x2f, 0x66, 0xd1, 0x68, 0xb4, 0x10, 0xbe, 0x8d, - 0xfa, 0x5f, 0xb5, 0x4d, 0x8b, 0x18, 0xf3, 0x16, 0xb4, 0xb0, 0xfb, 0x29, 0x85, 0x6f, 0x8b, 0x1f, - 0x7f, 0x03, 0xca, 0xd4, 0x45, 0x0b, 0x16, 0x0e, 0x2d, 0x02, 0x66, 0xf8, 0x83, 0x68, 0x80, 0xda, - 0x80, 0x77, 0x81, 0x73, 0x76, 0x53, 0xce, 0x0f, 0x70, 0xce, 0x27, 0x1c, 0x56, 0x28, 0xce, 0x3a, - 0x64, 0x47, 0xf5, 0x4a, 0x23, 0xba, 0x6b, 0x5b, 0xbc, 0xe7, 0x41, 0xaf, 0x1c, 0x80, 0x88, 0x7a, - 0xc5, 0x68, 0xa8, 0xe9, 0xca, 0x3e, 0x16, 0xba, 0x41, 0xd8, 0xbb, 0x30, 0x59, 0x45, 0x7b, 0x40, - 0x20, 0x56, 0xbf, 0x33, 0x8b, 0x1e, 0x0f, 0x45, 0xa6, 0x91, 0xbb, 0x26, 0x59, 0xe5, 0xe2, 0x5c, - 0x31, 0xdb, 0x7c, 0xf3, 0x48, 0x55, 0xde, 0x9d, 0x58, 0xd1, 0xad, 0x65, 0x62, 0xe0, 0x4b, 0xa8, - 0x97, 0xee, 0xf0, 0x5d, 0x25, 0x03, 0xe6, 0x1a, 0x4c, 0x27, 0x0e, 0x05, 0x88, 0xa7, 0x0f, 0x40, - 0x81, 0x6d, 0x54, 0x58, 0x70, 0x74, 0xd3, 0xf3, 0x7b, 0xb6, 0x14, 0xef, 0xd9, 0x2d, 0xd4, 0x78, - 0x99, 0xf1, 0x60, 0x73, 0x3e, 0x08, 0xc2, 0x03, 0x80, 0x28, 0x08, 0x46, 0x72, 0xe6, 0x39, 0x34, - 0x28, 0x10, 0x6f, 0x6b, 0x52, 0xff, 0x52, 0x5e, 0xd4, 0x75, 0xbf, 0x59, 0x5c, 0xd7, 0xaf, 0x50, - 0x1d, 0x75, 0x5d, 0x6a, 0x55, 0x30, 0x25, 0xe7, 0x9a, 0x08, 0x20, 0x59, 0x13, 0x01, 0x84, 0x9f, - 0x42, 0xfd, 0x8c, 0x45, 0xb0, 0x7f, 0x85, 0xbd, 0xaf, 0x03, 0x30, 0x79, 0x69, 0x0e, 0x08, 0xf1, - 0xcf, 0x66, 0xd0, 0xb9, 0xae, 0x92, 0x00, 0x65, 0x18, 0xbc, 0xfa, 0xf4, 0x8e, 0xc4, 0x38, 0xfe, - 0xf8, 0xc6, 0x7a, 0xf1, 0x52, 0x2b, 0x20, 0xa9, 0x3b, 0x02, 0x4d, 0xbd, 0xc1, 0x88, 0x84, 0x76, - 0x75, 0x6f, 0x0a, 0x35, 0x1e, 0x59, 0xa5, 0x53, 0x70, 0x86, 0x63, 0x35, 0xd6, 0xfc, 0x46, 0xe6, - 0x43, 0xe3, 0x91, 0x7f, 0xef, 0x92, 0x4f, 0x92, 0x50, 0x4d, 0x0a, 0x17, 0xdc, 0x40, 0xa7, 0x18, - 0xa6, 0xac, 0xaf, 0xcd, 0x2f, 0xcd, 0xda, 0x96, 0xb7, 0xe2, 0x57, 0xd0, 0x2b, 0x1e, 0x82, 0x40, - 0x05, 0x86, 0xbe, 0x56, 0xb7, 0x97, 0xea, 0x2d, 0x4a, 0x95, 0x50, 0x47, 0x1a, 0x27, 0x3a, 0xd1, - 0xf2, 0x31, 0xe7, 0x4f, 0x41, 0x85, 0xf0, 0x88, 0xca, 0x1f, 0xa7, 0xf1, 0x09, 0x27, 0x52, 0x48, - 0xad, 0xa0, 0xa1, 0x19, 0xbb, 0x71, 0x27, 0x50, 0x97, 0xe7, 0x50, 0x61, 0x41, 0x77, 0x96, 0x89, - 0x07, 0xb2, 0x18, 0xbc, 0x3a, 0x76, 0x99, 0x1d, 0xfb, 0x52, 0x22, 0x86, 0x18, 0x1f, 0xe1, 0xb3, - 0x41, 0xc1, 0x83, 0xdf, 0x1a, 0x2f, 0xa0, 0x7e, 0xad, 0x17, 0x0d, 0xf1, 0x23, 0x4a, 0x98, 0xcd, - 0xf1, 0xf3, 0xe1, 0xa1, 0x2f, 0x9f, 0xbe, 0x82, 0x63, 0x9a, 0xe0, 0x78, 0x69, 0x88, 0x32, 0xfb, - 0xbd, 0xf5, 0x62, 0x66, 0x63, 0xbd, 0xd8, 0xa3, 0xf5, 0x0b, 0x9b, 0xca, 0x70, 0xbd, 0x11, 0x16, - 0x58, 0xf1, 0xd0, 0x31, 0x52, 0x96, 0xad, 0x3f, 0x2f, 0xa3, 0x3e, 0xde, 0x06, 0xae, 0x71, 0xa7, - 0xc2, 0xb3, 0x0c, 0xe9, 0xa8, 0x35, 0x52, 0xda, 0x2f, 0x85, 0x5f, 0x44, 0x05, 0xb6, 0xb7, 0xe7, - 0x02, 0x38, 0x99, 0x7c, 0x16, 0x12, 0x29, 0xce, 0xcb, 0xe0, 0x69, 0x84, 0xc2, 0x7d, 0x7d, 0x70, - 0xb2, 0xcc, 0x39, 0xc4, 0x77, 0xfc, 0x11, 0x2e, 0x42, 0x59, 0xfc, 0x0c, 0x1a, 0x5a, 0x20, 0x4e, - 0xcb, 0xb4, 0xf4, 0x66, 0xcd, 0x7c, 0xd3, 0x3f, 0x5c, 0x86, 0x85, 0xd7, 0x35, 0xdf, 0x14, 0x47, - 0xae, 0x44, 0x87, 0x3f, 0x92, 0xb4, 0x6f, 0xee, 0x83, 0x86, 0x3c, 0xb8, 0xe9, 0x86, 0x32, 0xd2, - 0x9e, 0x84, 0x6d, 0xf4, 0x6b, 0x68, 0x58, 0xda, 0x32, 0xf1, 0xd3, 0xc3, 0x73, 0x71, 0xd6, 0xc2, - 0xfe, 0x2f, 0xc2, 0x56, 0xe6, 0x40, 0x35, 0xb9, 0x62, 0x99, 0x9e, 0xa9, 0x37, 0x27, 0xec, 0x56, - 0x4b, 0xb7, 0x0c, 0x65, 0x20, 0xd4, 0x64, 0x93, 0x61, 0xea, 0x0d, 0x86, 0x12, 0x35, 0x59, 0x2e, - 0x44, 0xb7, 0xe5, 0xbc, 0x0f, 0x35, 0xd2, 0xb0, 0x1d, 0x6a, 0x0b, 0xc0, 0xe1, 0x20, 0xdf, 0x96, - 0xbb, 0x0c, 0x57, 0x77, 0x7c, 0xa4, 0x68, 0x6c, 0x47, 0x0b, 0xbe, 0x9a, 0xef, 0x1f, 0x1c, 0x1d, - 0x8a, 0x9e, 0xe7, 0xaa, 0x3f, 0x93, 0x43, 0x83, 0x9c, 0x94, 0x2e, 0xa5, 0x47, 0x0a, 0xbe, 0x1b, - 0x05, 0x4f, 0x54, 0xd4, 0xc2, 0x5e, 0x29, 0xaa, 0xfa, 0xa9, 0x6c, 0x30, 0x1b, 0x55, 0x1d, 0xd3, - 0xda, 0xdd, 0x6c, 0x74, 0x01, 0xa1, 0x89, 0x95, 0x8e, 0x75, 0x87, 0xdd, 0x5b, 0x65, 0xc3, 0x7b, - 0xab, 0x86, 0xa9, 0x09, 0x18, 0x7c, 0x0e, 0xe5, 0xcb, 0x94, 0x3f, 0xed, 0x99, 0xa1, 0xf1, 0x81, - 0xb7, 0x18, 0xa7, 0xcc, 0xe3, 0x1a, 0x80, 0xe9, 0xe6, 0x6a, 0x7c, 0xcd, 0x23, 0xcc, 0x9c, 0xcd, - 0xb1, 0xcd, 0xd5, 0x22, 0x05, 0x68, 0x0c, 0x8e, 0xaf, 0xa1, 0xb1, 0x32, 0x69, 0xea, 0x6b, 0xb3, - 0x66, 0xb3, 0x69, 0xba, 0xa4, 0x61, 0x5b, 0x86, 0x0b, 0x42, 0xe6, 0xd5, 0xb5, 0x5c, 0x2d, 0x4e, - 0x80, 0x55, 0x54, 0x98, 0x5f, 0x5a, 0x72, 0x89, 0x07, 0xe2, 0xcb, 0x8d, 0x23, 0x3a, 0x39, 0xdb, - 0x00, 0xd1, 0x38, 0x46, 0xfd, 0x42, 0x86, 0xee, 0x5e, 0xdc, 0x3b, 0x9e, 0xdd, 0x0e, 0xb4, 0x7c, - 0x57, 0x22, 0xb9, 0x14, 0xda, 0x15, 0x59, 0xf8, 0xda, 0x63, 0xfc, 0x6b, 0xfb, 0xb8, 0x6d, 0x11, - 0x5a, 0x14, 0x89, 0x5f, 0x95, 0xdb, 0xe4, 0xab, 0xd4, 0x3f, 0xcf, 0xa2, 0x53, 0xbc, 0xc5, 0x13, - 0x4d, 0xb3, 0xbd, 0x68, 0xeb, 0x8e, 0xa1, 0x91, 0x06, 0x31, 0xef, 0x92, 0x83, 0x39, 0xf0, 0xe4, - 0xa1, 0x93, 0xdf, 0xc5, 0xd0, 0xb9, 0x0a, 0x1b, 0x41, 0x2a, 0x19, 0x38, 0xf0, 0x65, 0x46, 0xc5, - 0xe8, 0xc6, 0x7a, 0x71, 0xc8, 0x60, 0x60, 0x38, 0xf2, 0xd7, 0x44, 0x22, 0xaa, 0x24, 0x33, 0xc4, - 0x5a, 0xf6, 0x56, 0x40, 0x49, 0x7a, 0x99, 0x92, 0x34, 0x01, 0xa2, 0x71, 0x8c, 0xfa, 0x67, 0x59, - 0x74, 0x22, 0x2a, 0xf2, 0x1a, 0xb1, 0x8c, 0x23, 0x79, 0xbf, 0x3d, 0xf2, 0xfe, 0x46, 0x0e, 0xdd, - 0xcf, 0xcb, 0xd4, 0x56, 0x74, 0x87, 0x18, 0x65, 0xd3, 0x21, 0x0d, 0xcf, 0x76, 0xd6, 0x0e, 0xb0, - 0x01, 0xb5, 0x77, 0x62, 0xbf, 0x86, 0x0a, 0x7c, 0xfb, 0xcf, 0xd6, 0x99, 0x91, 0xa0, 0x25, 0x00, - 0x8d, 0xad, 0x50, 0xec, 0xe8, 0x20, 0xd2, 0x59, 0x85, 0xad, 0x74, 0xd6, 0xfb, 0xd0, 0x70, 0x20, - 0x7a, 0xd8, 0x88, 0xf6, 0x85, 0xd6, 0x96, 0xe1, 0x23, 0x60, 0x2f, 0xaa, 0xc9, 0x84, 0x50, 0x9b, - 0x0f, 0xa8, 0x94, 0xc1, 0x1a, 0x1a, 0xe6, 0xb5, 0x05, 0xe5, 0x4c, 0x43, 0x13, 0x89, 0xd4, 0xf5, - 0x3c, 0x3a, 0x93, 0xdc, 0xed, 0x1a, 0xd1, 0x8d, 0xa3, 0x5e, 0x7f, 0x57, 0xf6, 0x3a, 0x7e, 0x10, - 0xe5, 0xab, 0xba, 0xb7, 0xc2, 0xef, 0xc1, 0xe1, 0x4e, 0x78, 0xc9, 0x6c, 0x92, 0x7a, 0x5b, 0xf7, - 0x56, 0x34, 0x40, 0x09, 0x73, 0x06, 0x02, 0x8e, 0x09, 0x73, 0x86, 0xb0, 0xd8, 0x0f, 0x3e, 0x90, - 0xb9, 0x98, 0x4f, 0x5c, 0xec, 0xbf, 0x96, 0x4f, 0x9b, 0x57, 0x6e, 0x3b, 0xa6, 0x47, 0x8e, 0x34, - 0xec, 0x48, 0xc3, 0x76, 0xa9, 0x61, 0x7f, 0x90, 0x45, 0xc3, 0xc1, 0xa6, 0xe9, 0x0d, 0xd2, 0xd8, - 0x9f, 0xb5, 0x2a, 0xdc, 0xca, 0xe4, 0x76, 0xbd, 0x95, 0xd9, 0x8d, 0x42, 0xa9, 0xc1, 0x91, 0x27, - 0x33, 0x0d, 0x40, 0x62, 0xec, 0xc8, 0x33, 0x38, 0xe8, 0x7c, 0x10, 0xf5, 0xcd, 0xea, 0xf7, 0xcc, - 0x56, 0xa7, 0xc5, 0xad, 0x74, 0xf0, 0xeb, 0x6a, 0xe9, 0xf7, 0x34, 0x1f, 0xae, 0xfe, 0xcb, 0x0c, - 0x1a, 0xe1, 0x42, 0xe5, 0xcc, 0x77, 0x25, 0xd5, 0x50, 0x3a, 0xd9, 0x5d, 0x4b, 0x27, 0xb7, 0x73, - 0xe9, 0xa8, 0x3f, 0x96, 0x43, 0xca, 0x94, 0xd9, 0x24, 0x0b, 0x8e, 0x6e, 0xb9, 0x4b, 0xc4, 0xe1, - 0xdb, 0xe9, 0x49, 0xca, 0x6a, 0x57, 0x1f, 0x28, 0x4c, 0x29, 0xd9, 0x1d, 0x4d, 0x29, 0xef, 0x45, - 0x03, 0xbc, 0x31, 0x81, 0x4f, 0x21, 0x8c, 0x1a, 0xc7, 0x07, 0x6a, 0x21, 0x9e, 0x12, 0x97, 0xda, - 0x6d, 0xc7, 0xbe, 0x4b, 0x1c, 0x76, 0x4b, 0xc5, 0x89, 0x75, 0x1f, 0xa8, 0x85, 0x78, 0x81, 0x33, - 0xf1, 0xed, 0x45, 0x91, 0x33, 0x71, 0xb4, 0x10, 0x8f, 0x2f, 0xa2, 0xfe, 0x19, 0xbb, 0xa1, 0x83, - 0xa0, 0xd9, 0xb4, 0x32, 0xb4, 0xb1, 0x5e, 0xec, 0x6f, 0x72, 0x98, 0x16, 0x60, 0x29, 0x65, 0xd9, - 0x5e, 0xb5, 0x9a, 0xb6, 0xce, 0x9c, 0x5f, 0xfa, 0x19, 0xa5, 0xc1, 0x61, 0x5a, 0x80, 0xa5, 0x94, - 0x54, 0xe6, 0xe0, 0x54, 0xd4, 0x1f, 0xf2, 0x5c, 0xe2, 0x30, 0x2d, 0xc0, 0xaa, 0x5f, 0xc8, 0x53, - 0xed, 0x75, 0xcd, 0x37, 0x0f, 0xfd, 0xba, 0x10, 0x0e, 0x98, 0xde, 0x1d, 0x0c, 0x98, 0x43, 0x73, - 0x60, 0xa7, 0xfe, 0x45, 0x1f, 0x42, 0x5c, 0xfa, 0x93, 0x47, 0x9b, 0xc3, 0xdd, 0x69, 0x4d, 0x19, - 0x8d, 0x4d, 0x5a, 0x2b, 0xba, 0xd5, 0x20, 0x46, 0x78, 0x6c, 0x59, 0x80, 0xa1, 0x0d, 0x3e, 0xbd, - 0x84, 0x23, 0xc3, 0x73, 0x4b, 0x2d, 0x5e, 0x00, 0x3f, 0x89, 0x06, 0x2b, 0x96, 0x47, 0x1c, 0xbd, - 0xe1, 0x99, 0x77, 0x09, 0x9f, 0x1a, 0xe0, 0x66, 0xd8, 0x0c, 0xc1, 0x9a, 0x48, 0x83, 0xaf, 0xa1, - 0xa1, 0xaa, 0xee, 0x78, 0x66, 0xc3, 0x6c, 0xeb, 0x96, 0xe7, 0x2a, 0xfd, 0x30, 0xa3, 0x81, 0x85, - 0xd1, 0x16, 0xe0, 0x9a, 0x44, 0x85, 0x3f, 0x82, 0x06, 0x60, 0x6b, 0x0a, 0x8e, 0xd3, 0x03, 0x9b, - 0x5e, 0x1c, 0x3e, 0x14, 0xba, 0x07, 0xb2, 0xd3, 0x57, 0xb8, 0x01, 0x8e, 0xde, 0x1d, 0x06, 0x1c, - 0xf1, 0x07, 0x50, 0xdf, 0xa4, 0x65, 0x00, 0x73, 0xb4, 0x29, 0x73, 0x95, 0x33, 0x3f, 0x19, 0x32, - 0xb7, 0xdb, 0x11, 0xde, 0x3e, 0xbb, 0xe4, 0x51, 0x36, 0xf8, 0xf6, 0x8d, 0xb2, 0xa1, 0xb7, 0xe1, - 0x58, 0x7c, 0x78, 0xaf, 0x8e, 0xc5, 0x47, 0x76, 0x78, 0x2c, 0xae, 0xbe, 0x89, 0x06, 0xc7, 0xab, - 0x53, 0xc1, 0xe8, 0x3d, 0x8d, 0x72, 0x55, 0xee, 0xa9, 0x90, 0x67, 0xf6, 0x4c, 0xdb, 0x34, 0x34, - 0x0a, 0xc3, 0x97, 0x50, 0xff, 0x04, 0xb8, 0xbf, 0xf1, 0x5b, 0xc4, 0x3c, 0x5b, 0xff, 0x1a, 0x00, - 0x03, 0x2f, 0x58, 0x1f, 0x8d, 0x1f, 0x41, 0x7d, 0x55, 0xc7, 0x5e, 0x76, 0xf4, 0x16, 0x5f, 0x83, - 0xc1, 0x55, 0xa4, 0xcd, 0x40, 0x9a, 0x8f, 0x53, 0xbf, 0x2f, 0xe3, 0x9b, 0xed, 0xb4, 0x44, 0xad, - 0x03, 0x47, 0xf3, 0x50, 0x77, 0x3f, 0x2b, 0xe1, 0x32, 0x90, 0xe6, 0xe3, 0xf0, 0x25, 0xd4, 0x3b, - 0xe9, 0x38, 0xb6, 0x23, 0x3a, 0x9b, 0x13, 0x0a, 0x10, 0xaf, 0x7b, 0x81, 0x02, 0x3f, 0x8b, 0x06, - 0xd9, 0x9c, 0xc3, 0x4e, 0x34, 0x73, 0xdd, 0x6e, 0x4a, 0x45, 0x4a, 0xf5, 0x2b, 0x39, 0xc1, 0x66, - 0x63, 0x12, 0x3f, 0x84, 0xb7, 0x02, 0x4f, 0xa1, 0xdc, 0x78, 0x75, 0x8a, 0x4f, 0x80, 0xc7, 0xfd, - 0xa2, 0x82, 0xaa, 0x44, 0xca, 0x51, 0x6a, 0x7c, 0x16, 0xe5, 0xab, 0x54, 0x7d, 0x0a, 0xa0, 0x1e, - 0xfd, 0x1b, 0xeb, 0xc5, 0x7c, 0x9b, 0xea, 0x0f, 0x40, 0x01, 0x4b, 0x37, 0x33, 0x6c, 0xc7, 0xc4, - 0xb0, 0xe1, 0x3e, 0xe6, 0x2c, 0xca, 0x97, 0x9c, 0xe5, 0xbb, 0x7c, 0xd6, 0x02, 0xac, 0xee, 0x2c, - 0xdf, 0xd5, 0x00, 0x8a, 0xaf, 0x20, 0xa4, 0x11, 0xaf, 0xe3, 0x58, 0xf0, 0x0e, 0x64, 0x00, 0xce, - 0xdf, 0x60, 0x36, 0x74, 0x00, 0x5a, 0x6f, 0xd8, 0x06, 0xd1, 0x04, 0x12, 0xf5, 0xa7, 0xc2, 0x8b, - 0x9d, 0xb2, 0xe9, 0xde, 0x39, 0xea, 0xc2, 0x6d, 0x74, 0xa1, 0xce, 0x8f, 0x38, 0xe3, 0x9d, 0x54, - 0x44, 0xbd, 0x53, 0x4d, 0x7d, 0xd9, 0x85, 0x3e, 0xe4, 0xbe, 0x64, 0x4b, 0x14, 0xa0, 0x31, 0x78, - 0xa4, 0x9f, 0xfa, 0x37, 0xef, 0xa7, 0x1f, 0xec, 0x0d, 0x46, 0xdb, 0x1c, 0xf1, 0x56, 0x6d, 0xe7, - 0xa8, 0xab, 0xb6, 0xda, 0x55, 0x17, 0x50, 0x5f, 0xcd, 0x69, 0x08, 0x47, 0x17, 0xb0, 0x1f, 0x70, - 0x9d, 0x06, 0x3b, 0xb6, 0xf0, 0x91, 0x94, 0xae, 0xec, 0x7a, 0x40, 0xd7, 0x17, 0xd2, 0x19, 0xae, - 0xc7, 0xe9, 0x38, 0x92, 0xd3, 0x55, 0x6d, 0xc7, 0xe3, 0x1d, 0x17, 0xd0, 0xb5, 0x6d, 0xc7, 0xd3, - 0x7c, 0x24, 0x7e, 0x2f, 0x42, 0x0b, 0x13, 0x55, 0xdf, 0xd9, 0x7e, 0x20, 0xf4, 0x05, 0xe4, 0x5e, - 0xf6, 0x9a, 0x80, 0xc6, 0x0b, 0x68, 0x60, 0xbe, 0x4d, 0x1c, 0xb6, 0x15, 0x62, 0x2f, 0x3b, 0xde, - 0x13, 0x11, 0x2d, 0xef, 0xf7, 0xcb, 0xfc, 0xff, 0x80, 0x9c, 0xad, 0x2f, 0xb6, 0xff, 0x53, 0x0b, - 0x19, 0xe1, 0x67, 0x51, 0xa1, 0xc4, 0xec, 0xbc, 0x41, 0x60, 0x19, 0x88, 0x0c, 0xb6, 0xa0, 0x0c, - 0xc5, 0xf6, 0xec, 0x3a, 0xfc, 0xad, 0x71, 0x72, 0xf5, 0x12, 0x1a, 0x8d, 0x56, 0x83, 0x07, 0x51, - 0xdf, 0xc4, 0xfc, 0xdc, 0xdc, 0xe4, 0xc4, 0xc2, 0x68, 0x0f, 0xee, 0x47, 0xf9, 0xda, 0xe4, 0x5c, - 0x79, 0x34, 0xa3, 0xfe, 0x9c, 0x30, 0x83, 0x50, 0xd5, 0x3a, 0xba, 0x1a, 0xde, 0xd5, 0x7d, 0xcb, - 0x28, 0xdc, 0x87, 0xc2, 0x89, 0x41, 0xcb, 0xf4, 0x3c, 0x62, 0xf0, 0x55, 0x02, 0xee, 0x0b, 0xbd, - 0x7b, 0x5a, 0x0c, 0x8f, 0x1f, 0x43, 0xc3, 0x00, 0xe3, 0x57, 0x84, 0x6c, 0x7f, 0xcc, 0x0b, 0x38, - 0xf7, 0x34, 0x19, 0xa9, 0x7e, 0x35, 0xbc, 0x1d, 0x9e, 0x21, 0xfa, 0x41, 0xbd, 0x51, 0x7c, 0x87, - 0xf4, 0x97, 0xfa, 0x57, 0x79, 0xf6, 0x04, 0x84, 0x3d, 0xdc, 0xdb, 0x0f, 0x51, 0x86, 0x47, 0xba, - 0xb9, 0x6d, 0x1c, 0xe9, 0x3e, 0x86, 0x0a, 0xb3, 0xc4, 0x5b, 0xb1, 0x7d, 0xc7, 0x2f, 0xf0, 0xd0, - 0x6b, 0x01, 0x44, 0xf4, 0xd0, 0x63, 0x34, 0xf8, 0x0e, 0xc2, 0xfe, 0xab, 0xbc, 0xc0, 0x11, 0xdb, - 0x3f, 0x42, 0x3e, 0x15, 0xdb, 0xa7, 0xd4, 0xe0, 0x49, 0x2e, 0xf8, 0xd8, 0x9f, 0x08, 0x1c, 0xbd, - 0x05, 0x4f, 0xac, 0xbf, 0x5c, 0x2f, 0x16, 0x18, 0x8d, 0x96, 0xc0, 0x16, 0xbf, 0x86, 0x06, 0x66, - 0xa7, 0x4a, 0xfc, 0x85, 0x1e, 0xf3, 0x8a, 0x38, 0x1d, 0x48, 0xd1, 0x47, 0x04, 0x22, 0x81, 0xf7, - 0x36, 0xad, 0x25, 0x3d, 0xfe, 0x40, 0x2f, 0xe4, 0x42, 0xb5, 0x85, 0xbd, 0xdc, 0xe1, 0xa7, 0x0b, - 0x81, 0xb6, 0xc8, 0xef, 0x79, 0xa2, 0xb2, 0x62, 0xd8, 0x88, 0xb6, 0xf4, 0xef, 0x62, 0x74, 0xcf, - 0xa3, 0xb1, 0x52, 0xbb, 0xdd, 0x34, 0x89, 0x01, 0xfa, 0xa2, 0x75, 0x9a, 0xc4, 0xe5, 0x2e, 0x3f, - 0xf0, 0x18, 0x44, 0x67, 0xc8, 0x3a, 0xbc, 0x0b, 0xad, 0x3b, 0x1d, 0xd9, 0x3f, 0x33, 0x5e, 0x56, - 0xfd, 0x81, 0x2c, 0x3a, 0x39, 0xe1, 0x10, 0xdd, 0x23, 0xb3, 0x53, 0xa5, 0x52, 0x07, 0x7c, 0xe4, - 0x9a, 0x4d, 0x62, 0x2d, 0xef, 0xcf, 0xb0, 0x7e, 0x01, 0x8d, 0x04, 0x0d, 0xa8, 0x35, 0xec, 0x36, - 0x11, 0x1f, 0x56, 0x35, 0x7c, 0x4c, 0xdd, 0xa5, 0x28, 0x2d, 0x42, 0x8a, 0x6f, 0xa0, 0xe3, 0x01, - 0xa4, 0xd4, 0x6c, 0xda, 0xab, 0x1a, 0xe9, 0xb8, 0xcc, 0x31, 0xb6, 0x9f, 0x39, 0xc6, 0x86, 0x1c, - 0x74, 0x8a, 0xaf, 0x3b, 0x94, 0x40, 0x4b, 0x2a, 0xa5, 0x7e, 0x3e, 0x87, 0x4e, 0xdd, 0xd2, 0x9b, - 0xa6, 0x11, 0x8a, 0x46, 0x23, 0x6e, 0xdb, 0xb6, 0x5c, 0x72, 0x80, 0x46, 0xa9, 0x34, 0x14, 0xf2, - 0x7b, 0x32, 0x14, 0xe2, 0x5d, 0xd4, 0xbb, 0xeb, 0x2e, 0x2a, 0xec, 0xa8, 0x8b, 0xfe, 0x53, 0x06, - 0x8d, 0xfa, 0x8e, 0xff, 0xe2, 0x6b, 0x6a, 0xc1, 0x2b, 0x1d, 0x8e, 0x10, 0x23, 0x7e, 0xd0, 0x80, - 0xc7, 0x35, 0xd4, 0x37, 0x79, 0xaf, 0x6d, 0x3a, 0xc4, 0xdd, 0x82, 0x13, 0xf7, 0x39, 0x7e, 0x5c, - 0x32, 0x46, 0x58, 0x91, 0xd8, 0x49, 0x09, 0x03, 0xc3, 0x73, 0x3e, 0xf6, 0xf4, 0x61, 0xdc, 0x7f, - 0x22, 0xce, 0x9e, 0xf3, 0xf1, 0x27, 0x12, 0xd2, 0xfb, 0xcc, 0x90, 0x14, 0x3f, 0x84, 0x72, 0x0b, - 0x0b, 0x33, 0x7c, 0x26, 0x85, 0xa7, 0xf9, 0x9e, 0x27, 0xbe, 0x57, 0xa4, 0x58, 0xf5, 0x8f, 0xb3, - 0x08, 0x51, 0x55, 0x60, 0xc3, 0x75, 0x5f, 0x94, 0x70, 0x1c, 0xf5, 0xfb, 0x02, 0xe7, 0x6a, 0x18, - 0x78, 0xed, 0x47, 0x3b, 0x22, 0x5a, 0x77, 0xf0, 0x42, 0xa3, 0xe8, 0x3b, 0x92, 0xb3, 0x7b, 0x00, - 0xd8, 0xd9, 0x80, 0x23, 0xb9, 0xef, 0x3e, 0xfe, 0x5e, 0x34, 0xc0, 0x67, 0x3c, 0x5b, 0x3a, 0xff, - 0x6f, 0xf8, 0x40, 0x2d, 0xc4, 0x47, 0xa6, 0xd6, 0xc2, 0x2e, 0x16, 0x62, 0x5f, 0xbc, 0xac, 0x57, - 0x8e, 0xc4, 0xbb, 0xc7, 0xe2, 0xfd, 0x0c, 0x17, 0x2f, 0x7b, 0xc1, 0x73, 0x60, 0xc5, 0xbb, 0x67, - 0x67, 0xdf, 0xea, 0x1f, 0x64, 0x10, 0xa6, 0xcd, 0xaa, 0xea, 0xae, 0xbb, 0x6a, 0x3b, 0x06, 0x73, - 0x4e, 0xdf, 0x17, 0xc1, 0xec, 0xdd, 0x7d, 0xe5, 0x57, 0xfa, 0xd1, 0x71, 0xc9, 0xf1, 0xf7, 0x80, - 0x4f, 0x56, 0x97, 0xe4, 0xd1, 0xd4, 0xed, 0xd5, 0xcb, 0xc3, 0xe2, 0x85, 0x68, 0xaf, 0xf4, 0x00, - 0x4d, 0xb8, 0x09, 0x7d, 0x1c, 0x0d, 0xf1, 0x1f, 0x74, 0x85, 0xf6, 0x6f, 0xba, 0x60, 0x94, 0xba, - 0x14, 0xa0, 0x49, 0x68, 0xfc, 0x34, 0x1a, 0xa0, 0x03, 0x66, 0x19, 0xa2, 0x78, 0xf4, 0x85, 0x2f, - 0x4a, 0x0c, 0x1f, 0x28, 0xae, 0x27, 0x01, 0xa5, 0xf0, 0x8e, 0xa8, 0x7f, 0x0b, 0xef, 0x88, 0x3e, - 0x8a, 0x06, 0x4b, 0x96, 0x65, 0x7b, 0xb0, 0x49, 0x77, 0xf9, 0xd5, 0x44, 0xaa, 0x55, 0xfe, 0x10, - 0x3c, 0x8e, 0x0f, 0xe9, 0x13, 0xcd, 0x72, 0x91, 0x21, 0xbe, 0xea, 0xbf, 0x8a, 0x21, 0x0e, 0xf7, - 0x2a, 0x87, 0xeb, 0x19, 0x87, 0xc3, 0xe2, 0x8f, 0x62, 0xa0, 0xf3, 0x86, 0xab, 0x8e, 0xdd, 0xb6, - 0x5d, 0x62, 0x30, 0x41, 0x0d, 0x86, 0xa1, 0x06, 0xda, 0x1c, 0x01, 0xef, 0xd8, 0xa4, 0x88, 0x1a, - 0x52, 0x11, 0xbc, 0x84, 0x4e, 0xf8, 0x17, 0xc5, 0xc1, 0x8b, 0xc1, 0x4a, 0xd9, 0x55, 0x86, 0xe0, - 0x55, 0x12, 0x8e, 0x2a, 0x43, 0xa5, 0x3c, 0x7e, 0xde, 0xbf, 0x16, 0xf1, 0x9f, 0x1c, 0xd6, 0x4d, - 0x43, 0xec, 0xea, 0x44, 0x7e, 0xf8, 0x5b, 0xd0, 0xe0, 0xac, 0x7e, 0xaf, 0xdc, 0xe1, 0x67, 0x2f, - 0xc3, 0x5b, 0xbf, 0x7d, 0x69, 0xe9, 0xf7, 0xea, 0x06, 0x2f, 0x17, 0xb1, 0x29, 0x44, 0x96, 0xb8, - 0x8e, 0x4e, 0x56, 0x1d, 0xbb, 0x65, 0x7b, 0xc4, 0x88, 0x3c, 0xbe, 0x3b, 0x16, 0xbe, 0xd6, 0x6d, - 0x73, 0x8a, 0x7a, 0x97, 0x57, 0x78, 0x29, 0x6c, 0x70, 0x0b, 0x1d, 0x2b, 0xb9, 0x6e, 0xa7, 0x45, - 0xc2, 0x1b, 0xaa, 0xd1, 0x4d, 0x3f, 0xe3, 0x3d, 0xdc, 0x6b, 0xf9, 0x7e, 0x1d, 0x8a, 0xb2, 0x0b, - 0xaa, 0xba, 0x67, 0x8a, 0x35, 0xc2, 0xb7, 0x44, 0x79, 0xbf, 0x9a, 0xef, 0x1f, 0x19, 0x3d, 0xa6, - 0x9d, 0x8a, 0x37, 0x66, 0xc1, 0xf4, 0x9a, 0x44, 0xfd, 0x72, 0x06, 0xa1, 0x50, 0xc0, 0xf8, 0x71, - 0x39, 0x54, 0x50, 0x26, 0xbc, 0xe8, 0xe0, 0xd1, 0x0b, 0xa4, 0xd8, 0x40, 0xf8, 0x2c, 0xca, 0x43, - 0x84, 0x8b, 0x6c, 0x78, 0xb0, 0x7a, 0xc7, 0xb4, 0x0c, 0x0d, 0xa0, 0x14, 0x2b, 0x3c, 0x45, 0x07, - 0x2c, 0x5c, 0xea, 0x33, 0xab, 0xb0, 0x8c, 0x8e, 0xd5, 0x3a, 0x8b, 0x7e, 0xdd, 0xc2, 0xbb, 0x3a, - 0x08, 0xb4, 0xe1, 0x76, 0x16, 0x83, 0xc7, 0xa8, 0x52, 0x18, 0x13, 0xb9, 0x88, 0xfa, 0x85, 0x4c, - 0x64, 0x16, 0xdc, 0xc7, 0x45, 0xef, 0xe1, 0xb8, 0x9f, 0x46, 0x7c, 0x5a, 0x52, 0xff, 0x56, 0x16, - 0x0d, 0x56, 0x6d, 0xc7, 0xe3, 0x21, 0x43, 0x0e, 0xf6, 0x2a, 0x24, 0xec, 0x95, 0xf2, 0xdb, 0xd8, - 0x2b, 0x9d, 0x45, 0x79, 0xc1, 0x45, 0x99, 0xdd, 0x8b, 0x18, 0x86, 0xa3, 0x01, 0x54, 0xfd, 0xd6, - 0x2c, 0x42, 0x1f, 0x78, 0xf2, 0xc9, 0x43, 0x2c, 0x20, 0xf5, 0x47, 0x33, 0xe8, 0x18, 0xbf, 0xa8, - 0x13, 0x82, 0x6e, 0xf5, 0xf9, 0x57, 0xac, 0xe2, 0xb8, 0x64, 0x20, 0xcd, 0xc7, 0xd1, 0x25, 0x60, - 0xf2, 0x9e, 0xe9, 0xc1, 0x5d, 0x85, 0x10, 0x75, 0x8b, 0x70, 0x98, 0xb8, 0x04, 0xf8, 0x74, 0xf8, - 0x71, 0xff, 0x0a, 0x32, 0x17, 0xae, 0x7b, 0xb4, 0xc0, 0x64, 0xe2, 0x35, 0xa4, 0xfa, 0xc5, 0x3c, - 0xca, 0x4f, 0xde, 0x23, 0x8d, 0x03, 0xde, 0x35, 0xc2, 0xc1, 0x66, 0x7e, 0x97, 0x07, 0x9b, 0x3b, - 0xf1, 0xa9, 0x78, 0x39, 0xec, 0xcf, 0x82, 0x5c, 0x7d, 0xa4, 0xe7, 0xa3, 0xd5, 0xfb, 0x3d, 0x7d, - 0xf0, 0x5c, 0x72, 0xfe, 0x49, 0x0e, 0xe5, 0x6a, 0x13, 0xd5, 0x23, 0xbd, 0xd9, 0x57, 0xbd, 0xe9, - 0x7e, 0x67, 0xad, 0x06, 0xd7, 0x50, 0xfd, 0xa1, 0x97, 0x68, 0xe4, 0xc6, 0xe9, 0x1b, 0x39, 0x34, - 0x52, 0x9b, 0x5a, 0xa8, 0x0a, 0x27, 0xc1, 0x37, 0x98, 0x27, 0x1f, 0xf8, 0x94, 0xb1, 0x2e, 0x3d, - 0x1b, 0xb3, 0x67, 0x6e, 0x56, 0x2c, 0xef, 0x99, 0x6b, 0xb7, 0xf4, 0x66, 0x87, 0xc0, 0xd1, 0x0b, - 0xf3, 0xfb, 0x75, 0xcd, 0x37, 0xc9, 0xe7, 0xe1, 0xe1, 0xbf, 0xcf, 0x00, 0xbf, 0x80, 0x72, 0x37, - 0xb9, 0x47, 0x46, 0x1a, 0x9f, 0xa7, 0xae, 0x32, 0x3e, 0x74, 0x12, 0xcc, 0x75, 0x4c, 0x03, 0x38, - 0xd0, 0x52, 0xb4, 0xf0, 0x75, 0xbe, 0x00, 0x6f, 0xa9, 0xf0, 0xb2, 0x5f, 0xf8, 0x7a, 0xa5, 0x8c, - 0x6b, 0x68, 0xb0, 0x4a, 0x9c, 0x96, 0x09, 0x1d, 0xe5, 0xcf, 0xd9, 0xdd, 0x99, 0xd0, 0x9d, 0xca, - 0x60, 0x3b, 0x2c, 0x04, 0xcc, 0x44, 0x2e, 0xf8, 0x75, 0x84, 0x98, 0x8d, 0xb2, 0xc5, 0x40, 0x8e, - 0xe7, 0xc0, 0xee, 0x67, 0xa6, 0x65, 0x82, 0x8d, 0x27, 0x30, 0xc3, 0x77, 0xd0, 0xe8, 0xac, 0x6d, - 0x98, 0x4b, 0x26, 0x73, 0xbd, 0x84, 0x0a, 0x0a, 0x9b, 0x3b, 0x3c, 0x51, 0x53, 0xb2, 0x25, 0x94, - 0x4b, 0xaa, 0x26, 0xc6, 0x58, 0xfd, 0x87, 0xbd, 0x28, 0x4f, 0xbb, 0xfd, 0x68, 0xfc, 0xee, 0x66, - 0xfc, 0x96, 0xd0, 0xe8, 0x6d, 0xdb, 0xb9, 0x63, 0x5a, 0xcb, 0x81, 0x57, 0x3c, 0xdf, 0x9b, 0x82, - 0x27, 0xcf, 0x2a, 0xc3, 0xd5, 0x03, 0x07, 0x7a, 0x2d, 0x46, 0xbe, 0xc9, 0x08, 0x7e, 0x0e, 0x21, - 0xf6, 0xd6, 0x1d, 0x68, 0xfa, 0xc3, 0x60, 0x15, 0xec, 0x25, 0x3c, 0x38, 0xda, 0x8b, 0xc1, 0x2a, - 0x42, 0x62, 0xba, 0x09, 0x67, 0xbe, 0x10, 0x03, 0xe0, 0x77, 0x0f, 0x9b, 0x70, 0xf0, 0x85, 0x10, - 0x8d, 0x00, 0xe6, 0x15, 0x51, 0x45, 0x48, 0xb8, 0x5f, 0x42, 0x11, 0x41, 0x48, 0x93, 0x03, 0x0f, - 0x0f, 0x97, 0x70, 0xbd, 0xa4, 0x09, 0x3c, 0xf0, 0x33, 0x91, 0x0b, 0x70, 0x2c, 0x71, 0x4b, 0xbd, - 0xff, 0x0e, 0x1d, 0xa8, 0x86, 0x36, 0x73, 0xa0, 0x52, 0x3f, 0x95, 0x45, 0x03, 0xb5, 0xce, 0xa2, - 0xbb, 0xe6, 0x7a, 0xa4, 0x75, 0xc0, 0xd5, 0xd8, 0xdf, 0x5e, 0xe5, 0x13, 0xb7, 0x57, 0x0f, 0xf9, - 0x42, 0x11, 0xce, 0x1d, 0x03, 0x93, 0xce, 0x17, 0xc7, 0x2f, 0x67, 0xd1, 0x28, 0xbb, 0x38, 0x2b, - 0x9b, 0x6e, 0x63, 0x0f, 0x9c, 0xf9, 0xf7, 0x5f, 0x2a, 0xbb, 0xbb, 0x6c, 0xde, 0xc2, 0x13, 0x09, - 0xf5, 0xe3, 0x59, 0x34, 0x58, 0xea, 0x78, 0x2b, 0x25, 0x0f, 0x74, 0xeb, 0x50, 0xee, 0x4f, 0x7e, - 0x3b, 0x83, 0x8e, 0xd1, 0x86, 0x2c, 0xd8, 0x77, 0x88, 0xb5, 0x07, 0x07, 0x8f, 0xe2, 0x01, 0x62, - 0x76, 0x87, 0x07, 0x88, 0xbe, 0x2c, 0x73, 0xdb, 0x93, 0x25, 0x1c, 0x97, 0x6b, 0x76, 0x93, 0x1c, - 0xec, 0xcf, 0xd8, 0xc3, 0xe3, 0x72, 0x5f, 0x20, 0x7b, 0x70, 0x3d, 0xf3, 0xee, 0x12, 0xc8, 0x1e, - 0x9c, 0x2d, 0xbd, 0x3b, 0x04, 0xf2, 0x95, 0x0c, 0x1a, 0x18, 0xb7, 0xbd, 0x03, 0x3e, 0xf0, 0xf9, - 0x57, 0x1c, 0x6c, 0x35, 0xf7, 0xbf, 0xe2, 0x60, 0xeb, 0xa6, 0xfa, 0x43, 0x59, 0x74, 0x82, 0x07, - 0xe9, 0xe6, 0xe7, 0x0f, 0x47, 0xd3, 0x31, 0x1f, 0x6c, 0x71, 0xd1, 0x1c, 0xcd, 0x43, 0x5c, 0x34, - 0x3f, 0x9d, 0x43, 0x27, 0x20, 0x94, 0x29, 0xdd, 0x96, 0xbd, 0x0b, 0x6c, 0x11, 0xdc, 0x90, 0x2f, - 0x41, 0x67, 0x13, 0x2e, 0x41, 0xff, 0x72, 0xbd, 0xf8, 0xcc, 0xb2, 0xe9, 0xad, 0x74, 0x16, 0x2f, - 0x37, 0xec, 0xd6, 0x95, 0x65, 0x47, 0xbf, 0x6b, 0xb2, 0xeb, 0x3f, 0xbd, 0x79, 0x25, 0xc8, 0x77, - 0xa1, 0xb7, 0x4d, 0x9e, 0x09, 0xa3, 0x06, 0x7b, 0x1d, 0xca, 0xd5, 0xbf, 0x3e, 0x75, 0x11, 0x7a, - 0xd5, 0x36, 0x2d, 0xee, 0x53, 0xc8, 0x0c, 0xdd, 0x1a, 0xdd, 0x1f, 0xbe, 0x61, 0x9b, 0x56, 0x3d, - 0xea, 0x58, 0xb8, 0xdd, 0xfa, 0x42, 0xd6, 0x9a, 0x50, 0x8d, 0xfa, 0x2f, 0x32, 0xe8, 0xb4, 0xac, - 0xc5, 0xef, 0x06, 0xdb, 0xf1, 0x87, 0xb3, 0xe8, 0xbe, 0xeb, 0x20, 0x9c, 0xc0, 0x91, 0xe3, 0x68, - 0xde, 0xe2, 0x83, 0x33, 0x41, 0x36, 0x47, 0x16, 0x65, 0xba, 0x6c, 0x8e, 0x26, 0x75, 0x2e, 0x9b, - 0x7f, 0x9e, 0x41, 0xc7, 0xe7, 0x2b, 0xe5, 0x89, 0x77, 0xc9, 0x88, 0x8a, 0x7f, 0xcf, 0x01, 0x37, - 0x38, 0x63, 0xdf, 0x73, 0xc0, 0x4d, 0x4f, 0xfa, 0x3d, 0xb5, 0xd2, 0xec, 0xcc, 0xbb, 0x49, 0xdf, - 0xa4, 0xef, 0x79, 0x17, 0xe8, 0x9b, 0xf4, 0x3d, 0x07, 0x5c, 0xdf, 0xfe, 0x71, 0x01, 0x0d, 0xde, - 0xe8, 0x2c, 0x12, 0xee, 0x12, 0x72, 0xa8, 0xcf, 0x5b, 0xaf, 0xa2, 0x41, 0x2e, 0x06, 0xb8, 0xab, - 0x10, 0x42, 0xd6, 0xf1, 0x10, 0x24, 0x2c, 0x2a, 0x90, 0x48, 0x84, 0xcf, 0xa2, 0xfc, 0x2d, 0xe2, - 0x2c, 0x8a, 0xaf, 0x39, 0xef, 0x12, 0x67, 0x51, 0x03, 0x28, 0x9e, 0x09, 0x1d, 0xd5, 0x4b, 0xd5, - 0x0a, 0xa4, 0x2f, 0xe1, 0xd7, 0x24, 0x90, 0x8f, 0x25, 0xf0, 0x36, 0xd3, 0xdb, 0x26, 0x4b, 0x7c, - 0x22, 0xbe, 0x24, 0x8f, 0x96, 0xc4, 0x73, 0x68, 0x4c, 0x74, 0x37, 0x62, 0xb9, 0x3b, 0xfa, 0x13, - 0xd8, 0x25, 0x65, 0xed, 0x88, 0x17, 0xc5, 0x2f, 0xa3, 0x21, 0x1f, 0x08, 0x8e, 0x53, 0x03, 0x61, - 0xc0, 0xf8, 0x80, 0x55, 0x24, 0x31, 0x90, 0x54, 0x40, 0x64, 0x00, 0x87, 0xff, 0x28, 0x81, 0x41, - 0xc4, 0x11, 0x4d, 0x2a, 0x80, 0x9f, 0x06, 0x06, 0xf0, 0xb8, 0x02, 0x5c, 0x44, 0x06, 0xe1, 0xa9, - 0x23, 0x38, 0xc2, 0x3b, 0x1c, 0xce, 0x1e, 0xb4, 0x4a, 0x64, 0x78, 0x1e, 0xa1, 0xf0, 0x2a, 0x9f, - 0x87, 0x0d, 0xd8, 0xb6, 0x93, 0x81, 0xc0, 0x42, 0xbc, 0x84, 0x1b, 0xde, 0xc9, 0x25, 0x9c, 0xfa, - 0xfb, 0x59, 0x34, 0x58, 0x6a, 0xb7, 0x83, 0xa1, 0xf0, 0x38, 0x2a, 0x94, 0xda, 0xed, 0x9b, 0x5a, - 0x45, 0x0c, 0x20, 0xae, 0xb7, 0xdb, 0xf5, 0x8e, 0x63, 0x8a, 0x9e, 0x98, 0x8c, 0x08, 0x4f, 0xa0, - 0xe1, 0x52, 0xbb, 0x5d, 0xed, 0x2c, 0x36, 0xcd, 0x86, 0x90, 0x8f, 0x88, 0xa5, 0x4e, 0x6b, 0xb7, - 0xeb, 0x6d, 0xc0, 0x44, 0x93, 0x52, 0xc9, 0x65, 0xf0, 0x47, 0x21, 0xd8, 0x0e, 0x4f, 0x87, 0xc3, - 0x12, 0x6e, 0xa8, 0x41, 0xe8, 0xf0, 0xb0, 0x6d, 0x97, 0x03, 0x22, 0x16, 0x62, 0xfd, 0xac, 0x1f, - 0xa8, 0x9e, 0x56, 0x14, 0x4b, 0x7b, 0x13, 0xb2, 0xc4, 0x4f, 0xa0, 0xbe, 0x52, 0xbb, 0x2d, 0xdc, - 0xf2, 0x80, 0x2b, 0x0f, 0x2d, 0x15, 0xe9, 0x63, 0x9f, 0xec, 0xcc, 0x8b, 0x68, 0x44, 0xae, 0x6c, - 0x5b, 0x21, 0xda, 0xbf, 0x99, 0x81, 0x0f, 0x3a, 0xe0, 0x9e, 0xc4, 0x4f, 0xa1, 0x5c, 0xa9, 0xdd, - 0xe6, 0xf3, 0xd1, 0xf1, 0x84, 0xfe, 0x88, 0x3e, 0x3c, 0x2e, 0xb5, 0xdb, 0xfe, 0xa7, 0x1f, 0xf0, - 0x27, 0x09, 0x3b, 0xfa, 0xf4, 0xaf, 0xb0, 0x4f, 0x3f, 0xd8, 0xcf, 0x05, 0xd4, 0x2f, 0xe6, 0xd0, - 0xb1, 0x52, 0xbb, 0x7d, 0x14, 0xda, 0x7d, 0xaf, 0x9e, 0x37, 0x3f, 0x89, 0x90, 0x30, 0x3d, 0xf6, - 0x05, 0x0f, 0xa6, 0x06, 0x85, 0xa9, 0x51, 0xc9, 0x68, 0x02, 0x91, 0xaf, 0x7e, 0xfd, 0xdb, 0x52, - 0xbf, 0x8f, 0xe7, 0x60, 0x2a, 0x3e, 0xe8, 0xa1, 0x9a, 0xde, 0x29, 0xdd, 0xc6, 0xfb, 0xa0, 0xb0, - 0xad, 0x3e, 0xf8, 0x2d, 0x69, 0xf0, 0x40, 0xa8, 0xf0, 0xa3, 0x5e, 0xe8, 0xdd, 0x95, 0x59, 0x3c, - 0x22, 0x0a, 0x93, 0xc7, 0x8f, 0xf1, 0xd3, 0x17, 0xf1, 0x68, 0x46, 0x0d, 0x8a, 0xaa, 0x9b, 0x86, - 0x16, 0xa1, 0xf5, 0xfb, 0xb0, 0x6f, 0x5b, 0x7d, 0xb8, 0x9e, 0x85, 0x17, 0xcb, 0x41, 0x34, 0xa4, - 0xdd, 0xef, 0x2e, 0xae, 0x20, 0xc4, 0xee, 0xfb, 0x03, 0x67, 0xe2, 0x61, 0x16, 0xf8, 0x84, 0x65, - 0x35, 0xe2, 0x81, 0x4f, 0x42, 0x92, 0xc0, 0x2f, 0x29, 0x97, 0xe8, 0x97, 0x74, 0x09, 0xf5, 0x6b, - 0xfa, 0xea, 0x6b, 0x1d, 0xe2, 0xac, 0x71, 0x73, 0x86, 0x05, 0x1b, 0xd4, 0x57, 0xeb, 0x1f, 0xa3, - 0x40, 0x2d, 0x40, 0x63, 0x35, 0x78, 0xf2, 0x2e, 0xf8, 0x61, 0xb0, 0x93, 0xe9, 0xe0, 0xa1, 0xfb, - 0x4e, 0x14, 0x1d, 0x3f, 0x8f, 0x72, 0xa5, 0xdb, 0x35, 0x2e, 0xd9, 0xa0, 0x6b, 0x4b, 0xb7, 0x6b, - 0x5c, 0x5e, 0xa9, 0x65, 0x6f, 0xd7, 0xd4, 0x8f, 0x67, 0x11, 0x8e, 0x53, 0xe2, 0x67, 0xd0, 0x00, - 0x40, 0x97, 0xa9, 0xce, 0x88, 0xe9, 0x30, 0x57, 0xdd, 0xba, 0x03, 0x50, 0xc9, 0xb8, 0xf3, 0x49, - 0xf1, 0x73, 0x90, 0xf9, 0x97, 0x27, 0x64, 0x93, 0xd2, 0x61, 0xae, 0xba, 0x7e, 0xae, 0xdc, 0x48, - 0xe2, 0x5f, 0x4e, 0x0c, 0x76, 0xe1, 0xed, 0xda, 0xb4, 0xed, 0x7a, 0x5c, 0xd4, 0xcc, 0x2e, 0x5c, - 0x75, 0x21, 0x0f, 0xab, 0x64, 0x17, 0x32, 0x32, 0xc8, 0x25, 0x75, 0xbb, 0xc6, 0x1e, 0x87, 0x18, - 0x9a, 0xdd, 0xf4, 0x0d, 0x4a, 0x96, 0x4b, 0x6a, 0xd5, 0xad, 0xb3, 0x87, 0x25, 0x06, 0xa4, 0x1c, - 0x96, 0x72, 0x49, 0x49, 0xa5, 0xd4, 0x4f, 0xf7, 0xa3, 0xd1, 0xb2, 0xee, 0xe9, 0x8b, 0xba, 0x4b, - 0x84, 0xdd, 0xf4, 0x31, 0x1f, 0xe6, 0x7f, 0x8e, 0x20, 0x07, 0x63, 0x31, 0xe1, 0x6b, 0xa2, 0x05, - 0xf0, 0x0b, 0x21, 0xdf, 0x20, 0xd3, 0xa7, 0x98, 0x3a, 0x6c, 0xb1, 0xde, 0xe6, 0x60, 0x2d, 0x46, - 0x88, 0x1f, 0x43, 0x83, 0x3e, 0x8c, 0x6e, 0x00, 0x72, 0xa1, 0xce, 0x18, 0x8b, 0xd4, 0xfe, 0xd7, - 0x44, 0x34, 0x7e, 0x0e, 0x0d, 0xf9, 0x3f, 0x05, 0xd3, 0x9a, 0xe5, 0x41, 0x5b, 0x8c, 0xed, 0x9e, - 0x44, 0x52, 0xb1, 0x28, 0xcc, 0x6f, 0xbd, 0x52, 0xd1, 0x48, 0xaa, 0x31, 0x89, 0x14, 0x7f, 0x0c, - 0x8d, 0xf8, 0xbf, 0xf9, 0x86, 0x81, 0x65, 0x65, 0x7b, 0x2c, 0xc8, 0x68, 0x1c, 0x11, 0xeb, 0x65, - 0x99, 0x9c, 0x6d, 0x1d, 0xee, 0xf7, 0xb3, 0x67, 0x19, 0x8b, 0xf1, 0x9d, 0x43, 0xa4, 0x02, 0x5c, - 0x41, 0x63, 0x3e, 0x24, 0xd4, 0xd0, 0xbe, 0x70, 0xc7, 0x68, 0x2c, 0xd6, 0x13, 0x95, 0x34, 0x5e, - 0x0a, 0x37, 0xd1, 0x59, 0x09, 0x68, 0xb8, 0x2b, 0xe6, 0x92, 0xc7, 0xb7, 0x7b, 0x3c, 0xf2, 0x2f, - 0x4f, 0x97, 0x18, 0x70, 0x65, 0x34, 0x7e, 0xde, 0x53, 0x39, 0x27, 0x53, 0x57, 0x6e, 0xb8, 0x86, - 0x4e, 0xf8, 0xf8, 0xeb, 0x13, 0xd5, 0xaa, 0x63, 0xbf, 0x41, 0x1a, 0x5e, 0xa5, 0xcc, 0xb7, 0xcb, - 0x10, 0x11, 0xce, 0x58, 0xac, 0x2f, 0x37, 0xda, 0x54, 0x29, 0x28, 0x4e, 0x66, 0x9e, 0x58, 0x18, - 0xdf, 0x42, 0xf7, 0x09, 0xf0, 0x8a, 0xe5, 0x7a, 0xba, 0xd5, 0x20, 0x95, 0x32, 0xdf, 0x43, 0xc3, - 0x7e, 0x9e, 0x73, 0x35, 0x39, 0x52, 0x66, 0x9b, 0x5c, 0x1c, 0xbf, 0x88, 0x86, 0x7d, 0x04, 0xbb, - 0xbb, 0x1b, 0x84, 0xbb, 0x3b, 0x18, 0x92, 0xc6, 0x62, 0x3d, 0xfa, 0x86, 0x51, 0x26, 0x16, 0x35, - 0x0a, 0x12, 0xca, 0x0f, 0x49, 0x1a, 0xe5, 0xad, 0xb5, 0x13, 0x95, 0x11, 0x92, 0xcc, 0xbf, 0x1c, - 0x6a, 0xd4, 0xbc, 0x63, 0x2e, 0x9b, 0x6c, 0x27, 0xed, 0x3f, 0x5b, 0x5c, 0xac, 0xdb, 0x00, 0x4c, - 0xd2, 0x0f, 0x46, 0x7e, 0xa6, 0x84, 0x8e, 0x27, 0xe8, 0xd8, 0xb6, 0x76, 0x8c, 0x9f, 0xca, 0x86, - 0x8d, 0x38, 0xe0, 0xdb, 0xc6, 0x71, 0xd4, 0xef, 0x7f, 0x09, 0x37, 0x1e, 0x94, 0xb4, 0xa1, 0x19, - 0xe5, 0xe1, 0xe3, 0x25, 0x71, 0x1c, 0xf0, 0xad, 0xe4, 0x5e, 0x88, 0xe3, 0xad, 0x4c, 0x28, 0x8e, - 0x03, 0xbe, 0xbd, 0xfc, 0x9e, 0x7c, 0x38, 0x27, 0x1d, 0xed, 0x31, 0xf7, 0xca, 0x4c, 0x0e, 0xbd, - 0x4f, 0x0b, 0xdb, 0x78, 0x3e, 0x28, 0xaa, 0x66, 0xdf, 0xce, 0x54, 0x13, 0xbf, 0x88, 0x06, 0xab, - 0xb6, 0xeb, 0x2d, 0x3b, 0xc4, 0xad, 0x06, 0x91, 0xeb, 0xe1, 0xe9, 0x69, 0x9b, 0x83, 0xeb, 0x6d, - 0x69, 0xf6, 0x17, 0xc9, 0xd5, 0x3f, 0xcc, 0xc5, 0xb4, 0x81, 0x19, 0xae, 0x07, 0x52, 0x1b, 0xf6, - 0x60, 0xa8, 0xe3, 0xab, 0xe1, 0x2a, 0xc8, 0x2c, 0xfc, 0x5e, 0x21, 0x2c, 0xdf, 0x22, 0x37, 0xf0, - 0x65, 0x12, 0xfc, 0x21, 0x74, 0x4a, 0x02, 0x54, 0x75, 0x47, 0x6f, 0x11, 0x2f, 0xcc, 0x12, 0x08, - 0x81, 0x96, 0xfc, 0xd2, 0xf5, 0x76, 0x80, 0x16, 0x33, 0x0f, 0xa6, 0x70, 0x10, 0x54, 0xab, 0x6f, - 0x1b, 0x8e, 0xcd, 0x7f, 0x94, 0x47, 0x4a, 0x60, 0x5e, 0x06, 0x4f, 0x78, 0xf6, 0x71, 0x2a, 0x7f, - 0x47, 0x74, 0xae, 0x89, 0xc6, 0x42, 0x61, 0xd4, 0x3a, 0xad, 0x96, 0x0e, 0x1d, 0x4c, 0xcd, 0xd7, - 0x62, 0x94, 0x59, 0x48, 0xc8, 0x2c, 0xd6, 0x33, 0xdc, 0x62, 0xc5, 0xe1, 0x13, 0xa9, 0xba, 0xcb, - 0x58, 0x68, 0x71, 0xae, 0xf8, 0x33, 0x19, 0x74, 0xa2, 0xb4, 0xb4, 0x44, 0x1a, 0x1e, 0x31, 0xe6, - 0x17, 0xa9, 0xe9, 0x36, 0x61, 0x77, 0x2c, 0xcf, 0xb7, 0x96, 0x9f, 0x4f, 0xaf, 0x8e, 0x75, 0xd2, - 0xe5, 0xa4, 0xc2, 0xac, 0x25, 0x41, 0xc8, 0x01, 0x9d, 0x93, 0xd4, 0x6d, 0xa0, 0xa9, 0x37, 0x80, - 0x48, 0x4b, 0xac, 0xf7, 0xcc, 0x75, 0x74, 0x3a, 0x95, 0xe5, 0x66, 0xa6, 0x52, 0xaf, 0x68, 0x2a, - 0xfd, 0xab, 0x4c, 0xa8, 0xee, 0x11, 0x21, 0xe1, 0xcb, 0x08, 0x85, 0x20, 0xbe, 0x79, 0x1a, 0xd9, - 0x58, 0x2f, 0xa2, 0x50, 0x68, 0x9a, 0x40, 0x81, 0xe7, 0x51, 0x81, 0x8b, 0x85, 0xe5, 0x7d, 0x7d, - 0xef, 0x26, 0xbd, 0x70, 0x59, 0x94, 0x03, 0x6c, 0x8c, 0xf8, 0x37, 0x73, 0x36, 0x67, 0x9e, 0x43, - 0x83, 0x3b, 0xfd, 0xae, 0xcf, 0xe4, 0x10, 0x16, 0x77, 0x3a, 0xfb, 0x68, 0x06, 0xbe, 0x23, 0x06, - 0xcb, 0xce, 0xf2, 0xb6, 0x5c, 0x44, 0xfd, 0xf4, 0x13, 0x20, 0x13, 0x82, 0x10, 0xf9, 0xb4, 0xc3, - 0x61, 0x5a, 0x80, 0x0d, 0xc3, 0x0e, 0xf5, 0x25, 0x87, 0x1d, 0x52, 0xbf, 0x3f, 0x87, 0x4e, 0x8a, - 0x1d, 0x52, 0x26, 0x10, 0x4c, 0xfd, 0xa8, 0x53, 0xde, 0xc6, 0x4e, 0x51, 0x51, 0x81, 0x19, 0xb8, - 0x3c, 0xaa, 0x3d, 0x3b, 0x7c, 0x00, 0x88, 0xc6, 0x31, 0xea, 0xbf, 0xcf, 0xa2, 0xe1, 0xc0, 0x88, - 0xd0, 0x1d, 0xf7, 0x10, 0x77, 0xc7, 0xfb, 0xd0, 0x30, 0x04, 0x8e, 0x69, 0x11, 0x8b, 0x05, 0x57, - 0xe9, 0x15, 0xd2, 0x50, 0xf8, 0x08, 0x9e, 0x71, 0x48, 0x22, 0xa4, 0xda, 0xcf, 0xec, 0x0b, 0x21, - 0x9c, 0x0f, 0x33, 0x2e, 0x18, 0x5c, 0xfd, 0x89, 0x1c, 0x1a, 0xf2, 0xa5, 0x3c, 0x6e, 0x1e, 0xd4, - 0xdb, 0x84, 0xfd, 0x15, 0xf2, 0x15, 0x84, 0xaa, 0xb6, 0xe3, 0xe9, 0xcd, 0xb9, 0x50, 0xf3, 0xe1, - 0x18, 0xae, 0x0d, 0x50, 0x56, 0x46, 0x20, 0x81, 0xf5, 0x2b, 0x34, 0xde, 0xd8, 0xc4, 0xc4, 0xd6, - 0xaf, 0x00, 0xaa, 0x09, 0x14, 0xea, 0x6f, 0x64, 0xd1, 0x31, 0xbf, 0x93, 0x26, 0xef, 0x91, 0x46, - 0xe7, 0x30, 0xcf, 0x4d, 0xb2, 0xb4, 0x7b, 0x37, 0x95, 0xb6, 0xfa, 0x5f, 0x85, 0x89, 0x64, 0xa2, - 0x69, 0x1f, 0x4d, 0x24, 0x7f, 0x1d, 0x3a, 0xae, 0x7e, 0x7b, 0x0e, 0x9d, 0xf0, 0xa5, 0x3e, 0xd5, - 0xb1, 0x60, 0x03, 0x3b, 0xa1, 0x37, 0x9b, 0x87, 0x79, 0xcf, 0x37, 0xe8, 0x0b, 0x62, 0x9e, 0x47, - 0x62, 0xe3, 0xd9, 0xdf, 0x96, 0x38, 0xb8, 0x6e, 0x9b, 0x86, 0x26, 0x12, 0xe1, 0x97, 0xd1, 0x90, - 0xff, 0xb3, 0xe4, 0x2c, 0xfb, 0x1b, 0x3d, 0x38, 0x8e, 0x0e, 0x0a, 0xe9, 0x8e, 0xf4, 0xe0, 0x5c, - 0x2a, 0xa0, 0xfe, 0xc7, 0x02, 0x3a, 0x73, 0xdb, 0xb4, 0x0c, 0x7b, 0xd5, 0xf5, 0x93, 0x07, 0x1e, - 0xf8, 0xe3, 0x98, 0xfd, 0x4e, 0x1a, 0xf8, 0x1a, 0xba, 0x2f, 0x2a, 0x52, 0x27, 0x08, 0xe9, 0xcc, - 0x7b, 0x67, 0x95, 0x11, 0xd4, 0xfd, 0x34, 0x82, 0xfc, 0x4e, 0x47, 0x4b, 0x2e, 0x19, 0xcd, 0x43, - 0xd8, 0xb7, 0x95, 0x3c, 0x84, 0x8f, 0xa2, 0x42, 0xd9, 0x6e, 0xe9, 0xa6, 0x1f, 0x7a, 0x04, 0x46, - 0x71, 0x50, 0x2f, 0x60, 0x34, 0x4e, 0x41, 0xf9, 0xf3, 0x8a, 0xa1, 0xcb, 0x06, 0x42, 0xfe, 0x7e, - 0x01, 0x6a, 0xa5, 0x69, 0x22, 0x11, 0xb6, 0xd1, 0x30, 0xaf, 0x8e, 0xdf, 0xc0, 0x20, 0xd8, 0x3c, - 0x3d, 0xed, 0xcb, 0x28, 0x5d, 0xad, 0x2e, 0x4b, 0xe5, 0xd8, 0x36, 0x8a, 0xa5, 0x47, 0xe4, 0x1f, - 0xc3, 0xee, 0x62, 0x34, 0x99, 0xbf, 0x20, 0x04, 0x98, 0x64, 0x06, 0xe3, 0x42, 0x80, 0x59, 0x46, - 0x24, 0xc2, 0x93, 0x68, 0x0c, 0x02, 0xef, 0x06, 0x5b, 0x29, 0xaa, 0x12, 0x43, 0x60, 0x54, 0xc2, - 0xc1, 0x3e, 0x8b, 0xd5, 0x4b, 0x3f, 0xae, 0xde, 0xe0, 0x68, 0x2d, 0x5e, 0xe2, 0xcc, 0x2b, 0x08, - 0xc7, 0xdb, 0xbc, 0xad, 0xa3, 0xfd, 0x4f, 0x67, 0xc3, 0x7d, 0xdd, 0x41, 0x77, 0xce, 0xd8, 0x8b, - 0xc3, 0xec, 0x5f, 0xc8, 0xa0, 0xb1, 0x58, 0x20, 0x67, 0xfc, 0x14, 0x42, 0x0c, 0x22, 0x04, 0xcc, - 0x83, 0x08, 0x14, 0x61, 0x70, 0x67, 0xbe, 0x94, 0x84, 0x64, 0xf8, 0x0a, 0xea, 0x67, 0xbf, 0x78, - 0x90, 0x9b, 0x78, 0x91, 0x4e, 0xc7, 0x34, 0xb4, 0x80, 0x28, 0xac, 0x05, 0xee, 0x88, 0x72, 0x89, - 0x45, 0xbc, 0xb5, 0x76, 0x50, 0x0b, 0x25, 0xa3, 0x1d, 0x38, 0x14, 0x34, 0xb8, 0x64, 0xec, 0x57, - 0xd7, 0x15, 0x78, 0x4c, 0xec, 0xdc, 0x66, 0x31, 0xb1, 0x23, 0x73, 0x13, 0x0f, 0x82, 0xbd, 0x77, - 0x0f, 0x4b, 0x3e, 0x9b, 0x45, 0xc7, 0x82, 0x5a, 0xf7, 0xf1, 0x3a, 0xe2, 0x1d, 0x24, 0x92, 0xcf, - 0x64, 0x90, 0x32, 0x6e, 0x36, 0x9b, 0xa6, 0xb5, 0x5c, 0xb1, 0x96, 0x6c, 0xa7, 0x05, 0x93, 0xc7, - 0xfe, 0x1d, 0x77, 0xaa, 0xdf, 0x95, 0x41, 0x63, 0xbc, 0x41, 0x13, 0xba, 0x63, 0xec, 0xdf, 0x59, - 0x52, 0xb4, 0x25, 0xfb, 0xa7, 0x2f, 0xea, 0x97, 0xb2, 0x08, 0xcd, 0xd8, 0x8d, 0x3b, 0x07, 0xfc, - 0x65, 0xe1, 0x0b, 0xa8, 0xc0, 0x22, 0x0d, 0x71, 0x8d, 0x1d, 0xbb, 0xcc, 0x1e, 0x8c, 0xd2, 0x4f, - 0x63, 0x88, 0xf1, 0x51, 0x7e, 0x42, 0x5b, 0x60, 0x91, 0x8a, 0x94, 0x8c, 0xc6, 0x8b, 0xd0, 0x4a, - 0x29, 0x1d, 0xb7, 0x6a, 0x82, 0x4a, 0x29, 0x4c, 0xae, 0x74, 0x63, 0xbd, 0x98, 0x6f, 0xda, 0x8d, - 0x3b, 0x1a, 0xd0, 0xab, 0x7f, 0x95, 0x61, 0xb2, 0x3b, 0xe0, 0xaf, 0xeb, 0xfc, 0xcf, 0xcf, 0x6f, - 0xf3, 0xf3, 0xbf, 0x3b, 0x83, 0x4e, 0x68, 0xa4, 0x61, 0xdf, 0x25, 0xce, 0xda, 0x84, 0x6d, 0x90, - 0xeb, 0xc4, 0x22, 0xce, 0x7e, 0x8d, 0xa8, 0x7f, 0x00, 0x49, 0x04, 0xc2, 0xc6, 0xdc, 0x74, 0x89, - 0x71, 0x70, 0x12, 0x3c, 0xa8, 0xbf, 0xd2, 0x87, 0x94, 0x44, 0x0b, 0xf1, 0xc0, 0x5a, 0x45, 0xa9, - 0x66, 0x7f, 0x7e, 0xaf, 0xcc, 0xfe, 0xde, 0xed, 0x99, 0xfd, 0x85, 0xed, 0x9a, 0xfd, 0x7d, 0x5b, - 0x31, 0xfb, 0x5b, 0x51, 0xb3, 0xbf, 0x1f, 0xcc, 0xfe, 0xa7, 0xba, 0x9a, 0xfd, 0x93, 0x96, 0xb1, - 0x43, 0xa3, 0xff, 0xc0, 0x26, 0x1f, 0xdd, 0xc9, 0x6e, 0xe5, 0x22, 0x9d, 0x14, 0x1b, 0xb6, 0x63, - 0x10, 0x83, 0x6f, 0x52, 0xe0, 0x84, 0xdc, 0xe1, 0x30, 0x2d, 0xc0, 0xc6, 0x32, 0xb9, 0x0e, 0x6f, - 0x25, 0x93, 0xeb, 0x1e, 0x6c, 0x63, 0x3e, 0x95, 0x45, 0x63, 0x13, 0xc4, 0xf1, 0x58, 0x28, 0xc3, - 0xbd, 0x70, 0x52, 0x2a, 0xa1, 0x63, 0x02, 0x43, 0xb0, 0xc8, 0xb3, 0xa1, 0xe3, 0x55, 0x83, 0x38, - 0x5e, 0xd4, 0x6f, 0x2b, 0x4a, 0x4f, 0xab, 0xf7, 0xb3, 0x29, 0xf1, 0xb1, 0x1b, 0x54, 0xef, 0xc3, - 0x99, 0x20, 0x4d, 0xfe, 0x4b, 0x0b, 0xe8, 0x85, 0x04, 0x49, 0xf9, 0xed, 0x27, 0x48, 0x52, 0x7f, - 0x2e, 0x83, 0x2e, 0x68, 0xc4, 0x22, 0xab, 0xfa, 0x62, 0x93, 0x08, 0xcd, 0xe2, 0x2b, 0x03, 0x9d, - 0x35, 0x4c, 0xb7, 0xa5, 0x7b, 0x8d, 0x95, 0x5d, 0xc9, 0x68, 0x0a, 0x0d, 0x89, 0xf3, 0xd7, 0x36, - 0xe6, 0x36, 0xa9, 0x9c, 0xfa, 0xeb, 0x39, 0xd4, 0x37, 0x6e, 0x7b, 0xaf, 0xda, 0xbb, 0xcc, 0xd8, - 0x15, 0x4e, 0xf9, 0xd9, 0x6d, 0x9c, 0x8b, 0x3c, 0x01, 0x95, 0x0b, 0x41, 0xcc, 0xc1, 0xa9, 0x6f, - 0xd1, 0x8e, 0x05, 0x7b, 0xf7, 0xc9, 0xb6, 0x99, 0xab, 0xeb, 0x19, 0x34, 0x00, 0x51, 0x30, 0x84, - 0x93, 0x4b, 0x70, 0x99, 0xf5, 0x28, 0x30, 0x5a, 0x47, 0x48, 0x8a, 0x3f, 0x24, 0xc5, 0x5e, 0x2c, - 0xec, 0x3e, 0xb7, 0x97, 0x18, 0x86, 0x71, 0xcf, 0x52, 0x68, 0xa9, 0xdf, 0xc8, 0xa3, 0x21, 0xdf, - 0x51, 0x72, 0x9f, 0x7a, 0xf0, 0x71, 0x54, 0x98, 0xb6, 0x85, 0x80, 0xec, 0xe0, 0x58, 0xb9, 0x62, - 0xbb, 0x11, 0x8f, 0x51, 0x4e, 0x84, 0x9f, 0x42, 0xfd, 0x73, 0xb6, 0x21, 0xba, 0x05, 0xc3, 0x98, - 0xb6, 0x6c, 0x23, 0xf6, 0xac, 0x32, 0x20, 0xc4, 0x17, 0x50, 0x1e, 0x3c, 0xaa, 0x85, 0xa3, 0xe7, - 0x88, 0x17, 0x35, 0xe0, 0x05, 0xdd, 0x28, 0x6c, 0x57, 0x37, 0xfa, 0x76, 0xaa, 0x1b, 0xfd, 0x7b, - 0xab, 0x1b, 0xaf, 0xa3, 0x21, 0xa8, 0xc9, 0xcf, 0xe7, 0xb4, 0xf9, 0xf2, 0x76, 0x9a, 0xaf, 0x40, - 0xc3, 0xac, 0xdd, 0x3c, 0xab, 0x13, 0x2c, 0x3c, 0x12, 0xab, 0x88, 0xda, 0xa1, 0x5d, 0xa8, 0xdd, - 0x1f, 0x66, 0x50, 0xdf, 0x4d, 0xeb, 0x8e, 0x65, 0xaf, 0xee, 0x4e, 0xe3, 0x9e, 0x42, 0x83, 0x9c, - 0x8d, 0x30, 0xc7, 0xc3, 0x4b, 0xd9, 0x0e, 0x03, 0xd7, 0x81, 0x93, 0x26, 0x52, 0xe1, 0x17, 0x83, - 0x42, 0xf0, 0x68, 0x22, 0x17, 0xa6, 0x34, 0xf0, 0x0b, 0x35, 0xe4, 0x28, 0xec, 0x22, 0x39, 0x3e, - 0x8b, 0xf2, 0x65, 0xda, 0x54, 0x21, 0xa6, 0x27, 0x6d, 0x8a, 0x06, 0x50, 0xf5, 0x9f, 0x65, 0xd1, - 0x48, 0xe4, 0xf8, 0xe9, 0x51, 0x34, 0xc0, 0x8f, 0x7f, 0x4c, 0x3f, 0x2c, 0x3c, 0x3c, 0xaa, 0x08, - 0x80, 0x5a, 0x3f, 0xfb, 0xb3, 0x62, 0xe0, 0xf7, 0xa3, 0x3e, 0xdb, 0x85, 0xa5, 0x09, 0xbe, 0x65, - 0x24, 0x1c, 0x42, 0xf3, 0x35, 0xda, 0x76, 0x36, 0x38, 0x38, 0x89, 0xa8, 0x91, 0xb6, 0x0b, 0x9f, - 0x76, 0x0d, 0x0d, 0xe8, 0xae, 0x4b, 0xbc, 0xba, 0xa7, 0x2f, 0x8b, 0x91, 0xe2, 0x03, 0xa0, 0x38, - 0x3a, 0x00, 0xb8, 0xa0, 0x2f, 0xe3, 0x57, 0xd0, 0x70, 0xc3, 0x21, 0xb0, 0x78, 0xe9, 0x4d, 0xda, - 0x4a, 0xc1, 0xb8, 0x94, 0x10, 0xe2, 0x89, 0x7f, 0x88, 0xa8, 0x18, 0xf8, 0x16, 0x1a, 0xe6, 0x9f, - 0xc3, 0x3c, 0x9a, 0x61, 0xa0, 0x8d, 0x84, 0x8b, 0x09, 0x13, 0x09, 0xf3, 0x69, 0xe6, 0x8e, 0xed, - 0x22, 0xb9, 0xc8, 0xd7, 0x10, 0x48, 0xd5, 0xaf, 0x66, 0xa8, 0xc1, 0x43, 0x01, 0x90, 0x61, 0x95, - 0xea, 0x4a, 0x6b, 0x9b, 0xba, 0xd2, 0x0a, 0x73, 0xa1, 0x15, 0xdc, 0x2e, 0xb3, 0x93, 0xc6, 0xb1, - 0xf8, 0x32, 0x2a, 0x18, 0xe2, 0xd9, 0xcf, 0x49, 0xf9, 0x23, 0xfc, 0x7a, 0x34, 0x4e, 0x85, 0x2f, - 0xa2, 0x3c, 0x35, 0x68, 0xa3, 0x1b, 0x3f, 0x71, 0x8d, 0xd4, 0x80, 0x42, 0xfd, 0xd6, 0x2c, 0x1a, - 0x12, 0xbe, 0xe6, 0xea, 0xae, 0x3e, 0xe7, 0xf9, 0xad, 0x35, 0xd3, 0x77, 0x73, 0x80, 0x1d, 0x81, - 0xdf, 0xe4, 0x6b, 0x81, 0x28, 0xb6, 0x74, 0x05, 0xc1, 0x05, 0xf3, 0x0c, 0xff, 0xd0, 0xc2, 0xd6, - 0x37, 0x41, 0x94, 0xfe, 0xd5, 0x7c, 0x7f, 0x76, 0x34, 0xf7, 0x6a, 0xbe, 0x3f, 0x3f, 0xda, 0x0b, - 0x51, 0x75, 0x20, 0x90, 0x2d, 0xdb, 0x61, 0x5a, 0x4b, 0xe6, 0xf2, 0x01, 0xf7, 0x49, 0xdf, 0xdb, - 0x88, 0x43, 0x11, 0xd9, 0x1c, 0x70, 0x07, 0xf5, 0xb7, 0x55, 0x36, 0x47, 0xb9, 0xd3, 0xb8, 0x6c, - 0xfe, 0x28, 0x83, 0x94, 0x44, 0xd9, 0x94, 0xf6, 0xe9, 0xe6, 0x7b, 0xef, 0x32, 0xa8, 0x7d, 0x3d, - 0x8b, 0xc6, 0x2a, 0x96, 0x47, 0x96, 0xd9, 0xbe, 0xe7, 0x80, 0x4f, 0x15, 0x37, 0xd0, 0xa0, 0xf0, - 0x31, 0xbc, 0xcf, 0xef, 0x0f, 0x76, 0x95, 0x21, 0x2a, 0x85, 0x93, 0x58, 0x7a, 0x0f, 0x93, 0x2e, - 0x47, 0x84, 0x7c, 0xc0, 0xe7, 0x9c, 0x83, 0x21, 0xe4, 0x03, 0x3e, 0x79, 0xbd, 0x43, 0x85, 0xfc, - 0x9f, 0x33, 0xe8, 0x78, 0x42, 0xe5, 0xf8, 0x02, 0xea, 0xab, 0x75, 0x16, 0x21, 0x9c, 0x4f, 0x26, - 0xf4, 0x11, 0x75, 0x3b, 0x8b, 0x10, 0xc9, 0x47, 0xf3, 0x91, 0x78, 0x01, 0x1e, 0xed, 0xce, 0x57, - 0xca, 0x13, 0x5c, 0xaa, 0xaa, 0xf0, 0xfc, 0x98, 0x82, 0x93, 0xbe, 0x2c, 0x78, 0xd8, 0x6b, 0x9b, - 0x46, 0x23, 0xf2, 0xb0, 0x97, 0x96, 0xc1, 0x1f, 0x46, 0x03, 0xa5, 0x37, 0x3b, 0x0e, 0x01, 0xbe, - 0x4c, 0xe2, 0x0f, 0x07, 0x7c, 0x7d, 0x44, 0x12, 0x67, 0xf6, 0x46, 0x99, 0x52, 0x44, 0x79, 0x87, - 0x0c, 0xd5, 0x4f, 0x67, 0xd0, 0x99, 0xf4, 0xd6, 0xe1, 0x27, 0x50, 0x1f, 0xdd, 0xd9, 0x96, 0xb4, - 0x39, 0xfe, 0xe9, 0x2c, 0xdb, 0xa0, 0xdd, 0x24, 0x75, 0xdd, 0x11, 0x0d, 0x6f, 0x9f, 0x0c, 0xbf, - 0x84, 0x06, 0x2b, 0xae, 0xdb, 0x21, 0x4e, 0xed, 0xa9, 0x9b, 0x5a, 0x85, 0xef, 0xa9, 0xc0, 0x66, - 0x37, 0x01, 0x5c, 0x77, 0x9f, 0x8a, 0x04, 0xec, 0x11, 0xe9, 0xd5, 0x4f, 0x64, 0xd0, 0xd9, 0x6e, - 0x5f, 0x45, 0x37, 0xf0, 0x0b, 0xc4, 0xd2, 0x2d, 0xba, 0xe3, 0xcf, 0x84, 0x5b, 0x14, 0x0f, 0x60, - 0xf2, 0x26, 0x23, 0x20, 0xa4, 0x85, 0xd8, 0xe9, 0x58, 0x70, 0x1d, 0xcf, 0x4e, 0xf2, 0x00, 0x16, - 0x29, 0xe4, 0x13, 0xaa, 0xbf, 0x9b, 0x45, 0x43, 0xd5, 0x66, 0x67, 0xd9, 0x14, 0x16, 0x8e, 0x1d, - 0xdb, 0xdb, 0xbe, 0xf5, 0x9b, 0xdd, 0x9e, 0xf5, 0x4b, 0x87, 0x9b, 0xb3, 0xc3, 0xe1, 0xe6, 0x97, - 0xc3, 0x2f, 0xa2, 0x42, 0x1b, 0xbe, 0x23, 0x7a, 0x9e, 0xc8, 0xbe, 0x2e, 0xed, 0x3c, 0x91, 0x95, - 0xa1, 0xe3, 0xab, 0xb1, 0x8b, 0xf1, 0x15, 0x96, 0x15, 0x04, 0x1a, 0x2e, 0x12, 0x47, 0x02, 0xdd, - 0x13, 0x81, 0x86, 0x0b, 0xc2, 0x91, 0x40, 0x77, 0x21, 0xd0, 0x5f, 0xc9, 0xa2, 0x11, 0xb9, 0x4a, - 0xfc, 0x04, 0x1a, 0x64, 0xd5, 0xb0, 0x73, 0x95, 0x8c, 0xe0, 0x46, 0x1a, 0x82, 0x35, 0xc4, 0x7e, - 0xf0, 0x03, 0xa2, 0x63, 0x2b, 0xba, 0x5b, 0x0f, 0x4f, 0x38, 0xd8, 0x2d, 0x64, 0x3f, 0xf3, 0xe7, - 0x89, 0xa0, 0xb4, 0x91, 0x15, 0xdd, 0x9d, 0x08, 0x7f, 0xe3, 0x49, 0x84, 0x21, 0x4d, 0xbb, 0xcc, - 0x80, 0xe5, 0xdd, 0xe7, 0xa9, 0x5e, 0xa3, 0x58, 0x6d, 0x8c, 0xc1, 0x44, 0x36, 0x1f, 0x09, 0x9a, - 0x0d, 0xca, 0xd0, 0xbb, 0x85, 0x3c, 0xb4, 0x02, 0x7d, 0xf2, 0x31, 0x21, 0x23, 0x28, 0xeb, 0x9e, - 0xce, 0x36, 0xe5, 0x7e, 0x07, 0xa8, 0x7f, 0x66, 0xa2, 0xde, 0x79, 0x8b, 0xcc, 0x2f, 0xe1, 0x27, - 0xd1, 0x00, 0x55, 0x98, 0x19, 0x9b, 0xf6, 0x65, 0x86, 0x7b, 0x01, 0x08, 0x9a, 0x04, 0x88, 0xe9, - 0x1e, 0x2d, 0xa4, 0xc2, 0xd7, 0xc4, 0x4c, 0xec, 0x5c, 0xfb, 0xb0, 0x58, 0x86, 0x61, 0xa6, 0x7b, - 0x34, 0x31, 0x63, 0xfb, 0x35, 0x31, 0x03, 0x36, 0xd7, 0x3b, 0xa9, 0x14, 0xc3, 0xf8, 0xa5, 0xf8, - 0xf8, 0x98, 0x49, 0x4a, 0x13, 0x1d, 0xdd, 0x3d, 0xc5, 0x29, 0xa6, 0x7b, 0xb4, 0xe4, 0xf4, 0xd2, - 0x43, 0xa2, 0x0b, 0x61, 0xd4, 0x75, 0x41, 0xc4, 0x4d, 0xf7, 0x68, 0x12, 0x2d, 0x7e, 0x16, 0x0d, - 0xf2, 0xdf, 0xaf, 0xda, 0xa6, 0x15, 0x8d, 0x71, 0x22, 0xa0, 0xa6, 0x7b, 0x34, 0x91, 0x52, 0xa8, - 0xb4, 0xea, 0x98, 0x96, 0xc7, 0xdf, 0x3e, 0x46, 0x2b, 0x05, 0x9c, 0x50, 0x29, 0xfc, 0xc6, 0x2f, - 0xa1, 0xe1, 0x20, 0x78, 0xcc, 0x1b, 0xa4, 0xe1, 0xf1, 0xc3, 0xe3, 0xfb, 0x22, 0x85, 0x19, 0x72, - 0xba, 0x47, 0x93, 0xa9, 0xf1, 0x45, 0x54, 0xd0, 0x88, 0x6b, 0xbe, 0xe9, 0x5f, 0x7a, 0x8e, 0x08, - 0xe3, 0xdc, 0x7c, 0x93, 0x4a, 0x89, 0xe3, 0x69, 0xef, 0x84, 0xb7, 0xac, 0xfc, 0xa8, 0x17, 0x47, - 0x6a, 0x99, 0xb4, 0x0c, 0xda, 0x3b, 0xc2, 0x15, 0xfb, 0x2b, 0x61, 0x48, 0x1d, 0x9e, 0xfe, 0x6e, - 0x30, 0xfa, 0x76, 0x59, 0xc4, 0x4e, 0xf7, 0x68, 0x11, 0x7a, 0x41, 0xaa, 0x65, 0xd3, 0xbd, 0xc3, - 0xa3, 0x18, 0x46, 0xa5, 0x4a, 0x51, 0x82, 0x54, 0xe9, 0x4f, 0xa1, 0xea, 0x39, 0xe2, 0xad, 0xda, - 0xce, 0x1d, 0x1e, 0xb3, 0x30, 0x5a, 0x35, 0xc7, 0x0a, 0x55, 0x73, 0x88, 0x58, 0x35, 0x1d, 0x70, - 0x23, 0xc9, 0x55, 0xeb, 0x9e, 0x2e, 0x56, 0xcd, 0x4e, 0xe2, 0xfc, 0x4e, 0x9a, 0x21, 0xfa, 0x5d, - 0x96, 0x85, 0x38, 0xde, 0xa1, 0x80, 0x13, 0x3a, 0x14, 0x7e, 0xd3, 0x4a, 0x85, 0x4c, 0xb3, 0x3c, - 0xcd, 0x70, 0x50, 0xa9, 0x80, 0xa2, 0x95, 0x8a, 0x39, 0x69, 0xaf, 0x89, 0x09, 0x58, 0x95, 0x31, - 0xb9, 0x83, 0x42, 0x0c, 0xed, 0x20, 0x21, 0x51, 0x6b, 0x11, 0x92, 0x3b, 0x2a, 0x18, 0xc8, 0x07, - 0x83, 0x16, 0x4e, 0x54, 0xa7, 0x7b, 0x34, 0x48, 0xfb, 0xa8, 0xb2, 0xb4, 0xa1, 0xca, 0x71, 0xa0, - 0x18, 0xf2, 0x29, 0x28, 0x6c, 0xba, 0x47, 0x63, 0x29, 0x45, 0x9f, 0x14, 0x12, 0x74, 0x29, 0x27, - 0xe4, 0x29, 0x22, 0x40, 0xd0, 0x29, 0x22, 0x4c, 0xe3, 0x35, 0x15, 0x4f, 0x62, 0xa5, 0xdc, 0x27, - 0x2f, 0x35, 0x51, 0xfc, 0x74, 0x8f, 0x16, 0x4f, 0x7c, 0xf5, 0xac, 0x94, 0xd7, 0x49, 0x39, 0x19, - 0x09, 0x2c, 0x14, 0xa2, 0xa8, 0xb8, 0xc4, 0x0c, 0x50, 0xf3, 0x89, 0x99, 0xd8, 0x95, 0x53, 0xf2, - 0xc6, 0x25, 0x81, 0x64, 0xba, 0x47, 0x4b, 0xcc, 0xe1, 0x3e, 0x11, 0xcb, 0xae, 0xa4, 0x28, 0xb2, - 0x87, 0x47, 0x04, 0x3d, 0xdd, 0xa3, 0xc5, 0xf2, 0x31, 0x5d, 0x13, 0xd3, 0x1a, 0x29, 0xa7, 0xe5, - 0x4e, 0x0c, 0x31, 0xb4, 0x13, 0x85, 0xf4, 0x47, 0xd7, 0xc4, 0x54, 0x37, 0xca, 0x99, 0x78, 0xa9, - 0x70, 0xe6, 0x14, 0x52, 0xe2, 0x68, 0xc9, 0xd9, 0x3b, 0x94, 0xfb, 0x79, 0xfe, 0x44, 0x5e, 0x3e, - 0x89, 0x66, 0xba, 0x47, 0x4b, 0xce, 0xfc, 0xa1, 0x25, 0xa7, 0xbd, 0x50, 0xce, 0x76, 0xe3, 0x19, - 0xb4, 0x2e, 0x39, 0x65, 0x86, 0xde, 0x25, 0x09, 0x81, 0x72, 0x4e, 0x8e, 0x6a, 0x9a, 0x4a, 0x38, - 0xdd, 0xa3, 0x75, 0x49, 0x65, 0x70, 0x33, 0x25, 0x23, 0x80, 0x72, 0x5e, 0x4e, 0x9f, 0x9a, 0x48, - 0x34, 0xdd, 0xa3, 0xa5, 0xe4, 0x13, 0xb8, 0x99, 0x12, 0x30, 0x5e, 0x29, 0x76, 0x65, 0x1b, 0xc8, - 0x23, 0x25, 0xdc, 0xfc, 0x7c, 0x62, 0xac, 0x75, 0xe5, 0x01, 0x59, 0x75, 0x13, 0x48, 0xa8, 0xea, - 0x26, 0x45, 0x69, 0x9f, 0x4f, 0x0c, 0x0e, 0xae, 0x3c, 0xd8, 0x85, 0x61, 0xd0, 0xc6, 0xc4, 0xb0, - 0xe2, 0xf3, 0x89, 0xd1, 0xb9, 0x15, 0x55, 0x66, 0x98, 0x40, 0x42, 0x19, 0x26, 0xc5, 0xf5, 0x9e, - 0x4f, 0x0c, 0x27, 0xad, 0x3c, 0xd4, 0x85, 0x61, 0xd8, 0xc2, 0xa4, 0x40, 0xd4, 0xcf, 0x4a, 0xf1, - 0x9c, 0x95, 0x87, 0xe5, 0x79, 0x43, 0x40, 0xd1, 0x79, 0x43, 0x8c, 0xfc, 0x3c, 0x11, 0x8b, 0x58, - 0xa9, 0x3c, 0x22, 0x0f, 0xf3, 0x08, 0x9a, 0x0e, 0xf3, 0x68, 0x8c, 0xcb, 0x89, 0x58, 0xe4, 0x3e, - 0xe5, 0x42, 0x1a, 0x13, 0x40, 0xcb, 0x4c, 0x58, 0xac, 0xbf, 0x4a, 0x42, 0xe8, 0x38, 0xe5, 0x3d, - 0xb2, 0x77, 0x72, 0x8c, 0x60, 0xba, 0x47, 0x4b, 0x08, 0x38, 0xa7, 0x25, 0xc7, 0x49, 0x51, 0x2e, - 0xca, 0xc3, 0x36, 0x89, 0x86, 0x0e, 0xdb, 0xc4, 0x18, 0x2b, 0x33, 0x49, 0x2f, 0x11, 0x94, 0x4b, - 0xb2, 0x61, 0x16, 0xa7, 0xa0, 0x86, 0x59, 0xc2, 0x0b, 0x06, 0x2d, 0x39, 0x76, 0x87, 0xf2, 0x68, - 0xd7, 0x16, 0x02, 0x4d, 0x42, 0x0b, 0x59, 0x28, 0x8b, 0xd0, 0x76, 0xba, 0xd9, 0x6e, 0xda, 0xba, - 0xa1, 0xbc, 0x37, 0xd1, 0x76, 0x62, 0x48, 0xc1, 0x76, 0x62, 0x00, 0xba, 0xca, 0x8b, 0x9e, 0xfa, - 0xca, 0x63, 0xf2, 0x2a, 0x2f, 0xe2, 0xe8, 0x2a, 0x2f, 0x79, 0xf5, 0x4f, 0xc4, 0xbc, 0xda, 0x95, - 0xc7, 0x65, 0x05, 0x88, 0xa0, 0xa9, 0x02, 0x44, 0xfd, 0xe0, 0x3f, 0x9a, 0xee, 0x07, 0xae, 0x5c, - 0x06, 0x6e, 0x0f, 0xf8, 0xdc, 0xd2, 0xe8, 0xa6, 0x7b, 0xb4, 0x74, 0x5f, 0xf2, 0x4a, 0x82, 0x5b, - 0xb7, 0x72, 0x45, 0x56, 0xb0, 0x18, 0x01, 0x55, 0xb0, 0xb8, 0x33, 0x78, 0x25, 0xc1, 0x2f, 0x5b, - 0x79, 0x22, 0x95, 0x55, 0xf0, 0xcd, 0x09, 0xde, 0xdc, 0xd7, 0x44, 0xc7, 0x6a, 0xe5, 0x49, 0x79, - 0xb1, 0x0b, 0x31, 0x74, 0xb1, 0x13, 0x1c, 0xb0, 0xaf, 0x89, 0x2e, 0xc5, 0xca, 0xd5, 0x78, 0xa9, - 0x70, 0x89, 0x14, 0x5c, 0x8f, 0xb5, 0x64, 0x4f, 0x5c, 0xe5, 0x29, 0x59, 0xeb, 0x92, 0x68, 0xa8, - 0xd6, 0x25, 0x7a, 0xf1, 0x4e, 0xc5, 0x1d, 0x6a, 0x95, 0x6b, 0xd1, 0x4d, 0xb6, 0x8c, 0xa7, 0x96, - 0x4f, 0xcc, 0x09, 0xf7, 0x95, 0x68, 0x10, 0x2f, 0xe5, 0xe9, 0xc8, 0xb5, 0xaf, 0x84, 0xa5, 0xf6, - 0x6d, 0x24, 0xe8, 0xd7, 0x2b, 0xd1, 0xb8, 0x57, 0xca, 0x33, 0xc9, 0x1c, 0x02, 0x5d, 0x89, 0xc6, - 0xc9, 0x7a, 0x25, 0x1a, 0x2a, 0x4a, 0x79, 0x36, 0x99, 0x43, 0x20, 0xdd, 0x68, 0x68, 0xa9, 0x27, - 0x85, 0xe0, 0xd5, 0xca, 0xfb, 0x64, 0xd3, 0x31, 0x40, 0x50, 0xd3, 0x31, 0x0c, 0x71, 0xfd, 0xa4, - 0x10, 0xf4, 0x59, 0x79, 0x2e, 0x56, 0x24, 0x68, 0xac, 0x10, 0x1a, 0xfa, 0x49, 0x21, 0x58, 0xb2, - 0xf2, 0x7c, 0xac, 0x48, 0xd0, 0x3a, 0x21, 0xa4, 0xb2, 0xd1, 0xed, 0xc5, 0xa2, 0xf2, 0x82, 0x7c, - 0x18, 0x9c, 0x4e, 0x39, 0xdd, 0xa3, 0x75, 0x7b, 0xf9, 0xf8, 0xd1, 0x74, 0xf7, 0x64, 0xe5, 0x45, - 0x79, 0x08, 0xa7, 0xd1, 0xd1, 0x21, 0x9c, 0xea, 0xe2, 0xfc, 0x52, 0x24, 0x7a, 0x81, 0xf2, 0x92, - 0x3c, 0xc5, 0x49, 0x48, 0x3a, 0xc5, 0x45, 0x63, 0x1d, 0x48, 0xcf, 0xf2, 0x95, 0xf7, 0xcb, 0x53, - 0x9c, 0x88, 0xa3, 0x53, 0x9c, 0xf4, 0x84, 0x7f, 0x22, 0xf6, 0x5a, 0x5c, 0x79, 0x59, 0x9e, 0xe2, - 0x22, 0x68, 0x3a, 0xc5, 0x45, 0xdf, 0x97, 0xbf, 0x14, 0x79, 0x34, 0xad, 0xbc, 0x92, 0xdc, 0x7e, - 0x40, 0x8a, 0xed, 0x67, 0x4f, 0xac, 0xb5, 0xe4, 0xd7, 0xbf, 0x4a, 0x49, 0x1e, 0xbf, 0x49, 0x34, - 0x74, 0xfc, 0x26, 0xbe, 0x1c, 0x8e, 0x6e, 0x1c, 0xb8, 0x56, 0x8d, 0x77, 0xd9, 0x38, 0x84, 0xa6, - 0x48, 0x02, 0x58, 0xda, 0x23, 0xb3, 0x8d, 0xd0, 0x44, 0xca, 0x1e, 0xd9, 0xdf, 0x06, 0x45, 0xe8, - 0xe9, 0xec, 0x1a, 0xf3, 0x96, 0x55, 0xca, 0xf2, 0xec, 0x1a, 0x23, 0xa0, 0xb3, 0x6b, 0xdc, 0xc7, - 0x76, 0x0a, 0x8d, 0x72, 0x2d, 0x62, 0x4e, 0xc0, 0xa6, 0xb5, 0xac, 0x4c, 0x46, 0x1e, 0xdf, 0x45, - 0xf0, 0x74, 0x76, 0x8a, 0xc2, 0x60, 0xbd, 0x66, 0xb0, 0x89, 0xa6, 0xd9, 0x5e, 0xb4, 0x75, 0xc7, - 0xa8, 0x11, 0xcb, 0x50, 0xa6, 0x22, 0xeb, 0x75, 0x02, 0x0d, 0xac, 0xd7, 0x09, 0x70, 0x08, 0x3d, - 0x15, 0x81, 0x6b, 0xa4, 0x41, 0xcc, 0xbb, 0x44, 0xb9, 0x0e, 0x6c, 0x8b, 0x69, 0x6c, 0x39, 0xd9, - 0x74, 0x8f, 0x96, 0xc6, 0x81, 0xda, 0xea, 0xb3, 0x6b, 0xb5, 0xd7, 0x66, 0x82, 0x07, 0xe7, 0x55, - 0x87, 0xb4, 0x75, 0x87, 0x28, 0xd3, 0xb2, 0xad, 0x9e, 0x48, 0x44, 0x6d, 0xf5, 0x44, 0x44, 0x9c, - 0xad, 0x3f, 0x16, 0x2a, 0xdd, 0xd8, 0x86, 0x23, 0x22, 0xb9, 0x34, 0x9d, 0x9d, 0x64, 0x04, 0x15, - 0xd0, 0x8c, 0x6d, 0x2d, 0xc3, 0x49, 0xc5, 0xab, 0xf2, 0xec, 0x94, 0x4e, 0x49, 0x67, 0xa7, 0x74, - 0x2c, 0x55, 0x75, 0x19, 0xcb, 0xc6, 0xe0, 0x0d, 0x59, 0xd5, 0x13, 0x48, 0xa8, 0xaa, 0x27, 0x80, - 0xe3, 0x0c, 0x35, 0xe2, 0x12, 0x4f, 0x99, 0xe9, 0xc6, 0x10, 0x48, 0xe2, 0x0c, 0x01, 0x1c, 0x67, - 0x38, 0x45, 0xbc, 0xc6, 0x8a, 0x32, 0xdb, 0x8d, 0x21, 0x90, 0xc4, 0x19, 0x02, 0x98, 0x6e, 0x36, - 0x65, 0xf0, 0x78, 0xa7, 0x79, 0xc7, 0xef, 0xb3, 0x39, 0x79, 0xb3, 0x99, 0x4a, 0x48, 0x37, 0x9b, - 0xa9, 0x48, 0xfc, 0x89, 0x2d, 0x7b, 0x73, 0x2b, 0xf3, 0x50, 0xe1, 0xe5, 0xd0, 0x2e, 0xd8, 0x4a, - 0xa9, 0xe9, 0x1e, 0x6d, 0xab, 0xde, 0xe2, 0xef, 0x0d, 0x9c, 0x2e, 0x95, 0x2a, 0x54, 0x75, 0x2c, - 0x38, 0xab, 0x60, 0xe0, 0xe9, 0x1e, 0x2d, 0x70, 0xcb, 0x7c, 0x16, 0x0d, 0xc2, 0x47, 0x55, 0x2c, - 0xd3, 0x2b, 0x8f, 0x2b, 0xaf, 0xc9, 0x5b, 0x26, 0x01, 0x45, 0xb7, 0x4c, 0xc2, 0x4f, 0x3a, 0x89, - 0xc3, 0x4f, 0x36, 0xc5, 0x94, 0xc7, 0x15, 0x4d, 0x9e, 0xc4, 0x25, 0x24, 0x9d, 0xc4, 0x25, 0x40, - 0x50, 0x6f, 0xd9, 0xb1, 0xdb, 0xe5, 0x71, 0xa5, 0x96, 0x50, 0x2f, 0x43, 0x05, 0xf5, 0xb2, 0x9f, - 0x41, 0xbd, 0xb5, 0x95, 0x8e, 0x57, 0xa6, 0xdf, 0xb8, 0x90, 0x50, 0xaf, 0x8f, 0x0c, 0xea, 0xf5, - 0x01, 0x74, 0x2a, 0x04, 0x40, 0xd5, 0xb1, 0xe9, 0xa4, 0x7d, 0xc3, 0x6c, 0x36, 0x95, 0x9b, 0xf2, - 0x54, 0x18, 0xc5, 0xd3, 0xa9, 0x30, 0x0a, 0xa3, 0xa6, 0x27, 0x6b, 0x15, 0x59, 0xec, 0x2c, 0x2b, - 0xb7, 0x64, 0xd3, 0x33, 0xc4, 0x50, 0xd3, 0x33, 0xfc, 0x05, 0xbb, 0x0b, 0xfa, 0x4b, 0x23, 0x4b, - 0x0e, 0x71, 0x57, 0x94, 0xdb, 0x91, 0xdd, 0x85, 0x80, 0x83, 0xdd, 0x85, 0xf0, 0x1b, 0x2f, 0xa3, - 0xfb, 0xa5, 0x85, 0xc6, 0xbf, 0xb4, 0xa9, 0x11, 0xdd, 0x69, 0xac, 0x28, 0x1f, 0x00, 0x56, 0x0f, - 0x25, 0x2e, 0x55, 0x32, 0xe9, 0x74, 0x8f, 0xd6, 0x8d, 0x13, 0x6c, 0xcb, 0x5f, 0x9b, 0x61, 0x11, - 0x26, 0xb5, 0xea, 0x84, 0xbf, 0x09, 0x7d, 0x3d, 0xb2, 0x2d, 0x8f, 0x93, 0xc0, 0xb6, 0x3c, 0x0e, - 0xc6, 0x6d, 0x74, 0x3e, 0xb2, 0x55, 0x9b, 0xd5, 0x9b, 0x74, 0x5f, 0x42, 0x8c, 0xaa, 0xde, 0xb8, - 0x43, 0x3c, 0xe5, 0x83, 0xc0, 0xfb, 0x42, 0xca, 0x86, 0x2f, 0x42, 0x3d, 0xdd, 0xa3, 0x6d, 0xc2, - 0x0f, 0xab, 0x28, 0x5f, 0x9b, 0x5a, 0xa8, 0x2a, 0x1f, 0x92, 0xcf, 0x37, 0x29, 0x6c, 0xba, 0x47, - 0x03, 0x1c, 0xb5, 0xd2, 0x6e, 0xb6, 0x97, 0x1d, 0xdd, 0x20, 0xcc, 0xd0, 0x02, 0xdb, 0x8d, 0x1b, - 0xa0, 0x1f, 0x96, 0xad, 0xb4, 0x34, 0x3a, 0x6a, 0xa5, 0xa5, 0xe1, 0xa8, 0xa2, 0x4a, 0xc9, 0x14, - 0x94, 0x8f, 0xc8, 0x8a, 0x2a, 0x21, 0xa9, 0xa2, 0xca, 0xa9, 0x17, 0x3e, 0x80, 0x4e, 0x06, 0xfb, - 0x79, 0xbe, 0xfe, 0xb2, 0x4e, 0x53, 0x3e, 0x0a, 0x7c, 0xce, 0xc7, 0x2e, 0x03, 0x24, 0xaa, 0xe9, - 0x1e, 0x2d, 0xa5, 0x3c, 0x5d, 0x71, 0x63, 0x79, 0x82, 0xb8, 0x79, 0xf1, 0x2d, 0xf2, 0x8a, 0x9b, - 0x42, 0x46, 0x57, 0xdc, 0x14, 0x54, 0x22, 0x73, 0x2e, 0x54, 0x7d, 0x13, 0xe6, 0x81, 0x4c, 0xd3, - 0x38, 0x24, 0x32, 0xe7, 0x96, 0xda, 0xe2, 0x26, 0xcc, 0x03, 0x6b, 0x2d, 0x8d, 0x03, 0xbe, 0x88, - 0x0a, 0xb5, 0xda, 0xac, 0xd6, 0xb1, 0x94, 0x46, 0xc4, 0x5b, 0x16, 0xa0, 0xd3, 0x3d, 0x1a, 0xc7, - 0x53, 0x33, 0x68, 0xb2, 0xa9, 0xbb, 0x9e, 0xd9, 0x70, 0x61, 0xc4, 0xf8, 0x23, 0xc4, 0x90, 0xcd, - 0xa0, 0x24, 0x1a, 0x6a, 0x06, 0x25, 0xc1, 0xa9, 0xbd, 0x38, 0xa1, 0xbb, 0xae, 0x6e, 0x19, 0x8e, - 0x3e, 0x0e, 0xcb, 0x04, 0x89, 0xbc, 0x29, 0x92, 0xb0, 0xd4, 0x5e, 0x94, 0x21, 0x70, 0xf8, 0xee, - 0x43, 0x7c, 0x33, 0x67, 0x29, 0x72, 0xf8, 0x1e, 0xc1, 0xc3, 0xe1, 0x7b, 0x04, 0x06, 0x76, 0xa7, - 0x0f, 0xd3, 0xc8, 0xb2, 0x49, 0x45, 0xa4, 0x2c, 0x47, 0xec, 0xce, 0x28, 0x01, 0xd8, 0x9d, 0x51, - 0xa0, 0xd4, 0x24, 0x7f, 0xb9, 0x5d, 0x49, 0x69, 0x52, 0xb8, 0xca, 0xc6, 0xca, 0xd0, 0xf5, 0x3b, - 0x1c, 0x1c, 0xe5, 0x35, 0x4b, 0x6f, 0xd9, 0xe5, 0x71, 0x5f, 0xea, 0xa6, 0xbc, 0x7e, 0xa7, 0x12, - 0xd2, 0xf5, 0x3b, 0x15, 0x49, 0x67, 0x57, 0x7f, 0xa3, 0xb5, 0xa2, 0x3b, 0xc4, 0x28, 0x9b, 0x0e, - 0x9c, 0x2c, 0xae, 0xb1, 0xad, 0xe1, 0x1b, 0xf2, 0xec, 0xda, 0x85, 0x94, 0xce, 0xae, 0x5d, 0xd0, - 0xd4, 0xc8, 0x4b, 0x46, 0x6b, 0x44, 0x37, 0x94, 0x3b, 0xb2, 0x91, 0x97, 0x4e, 0x49, 0x8d, 0xbc, - 0x74, 0x6c, 0xfa, 0xe7, 0xdc, 0x76, 0x4c, 0x8f, 0x28, 0xcd, 0xad, 0x7c, 0x0e, 0x90, 0xa6, 0x7f, - 0x0e, 0xa0, 0xe9, 0x86, 0x30, 0xda, 0x21, 0x2d, 0x79, 0x43, 0x18, 0xef, 0x86, 0x68, 0x09, 0x6a, - 0xb1, 0xf0, 0xa7, 0x65, 0x8a, 0x25, 0x5b, 0x2c, 0x1c, 0x4c, 0x2d, 0x96, 0xf0, 0xf1, 0x99, 0xf4, - 0x94, 0x49, 0xb1, 0xe5, 0x35, 0x54, 0xc4, 0xd1, 0x35, 0x54, 0x7a, 0xf6, 0xf4, 0xac, 0xf4, 0xce, - 0x40, 0x69, 0xcb, 0x56, 0x87, 0x80, 0xa2, 0x56, 0x87, 0xf8, 0x22, 0x61, 0x02, 0x1d, 0x83, 0x5b, - 0x70, 0xad, 0x13, 0xdc, 0xe3, 0x7c, 0x4c, 0xfe, 0xcc, 0x08, 0x9a, 0x7e, 0x66, 0x04, 0x24, 0x31, - 0xe1, 0xd3, 0x96, 0x93, 0xc2, 0x24, 0x3c, 0x1f, 0x8c, 0x80, 0xf0, 0x0c, 0xc2, 0xb5, 0xd2, 0xec, - 0x4c, 0xc5, 0xa8, 0x8a, 0x57, 0x64, 0xae, 0x7c, 0x02, 0x1b, 0xa7, 0x98, 0xee, 0xd1, 0x12, 0xca, - 0xe1, 0x37, 0xd0, 0x59, 0x0e, 0xe5, 0xef, 0x86, 0x21, 0x89, 0xb7, 0x11, 0x2c, 0x08, 0x9e, 0xec, - 0xc7, 0xd6, 0x8d, 0x76, 0xba, 0x47, 0xeb, 0xca, 0x2b, 0xbd, 0x2e, 0xbe, 0x3e, 0x74, 0xb6, 0x52, - 0x57, 0xb0, 0x48, 0x74, 0xe5, 0x95, 0x5e, 0x17, 0x97, 0xfb, 0xdd, 0xad, 0xd4, 0x15, 0x74, 0x42, - 0x57, 0x5e, 0xd8, 0x45, 0xc5, 0x6e, 0xf8, 0x52, 0xb3, 0xa9, 0xac, 0x42, 0x75, 0xef, 0xd9, 0x4a, - 0x75, 0x25, 0x30, 0x38, 0x37, 0xe3, 0x48, 0x67, 0xe9, 0xf9, 0x36, 0xb1, 0x6a, 0xd2, 0x02, 0x74, - 0x4f, 0x9e, 0xa5, 0x63, 0x04, 0x74, 0x96, 0x8e, 0x01, 0xe9, 0x80, 0x12, 0x9f, 0xab, 0x28, 0x6b, - 0xf2, 0x80, 0x12, 0x71, 0x74, 0x40, 0x49, 0x4f, 0x5b, 0xe6, 0xd1, 0xf1, 0xf9, 0x3b, 0x9e, 0xee, - 0x5b, 0x90, 0x2e, 0xef, 0xca, 0x37, 0x23, 0x97, 0x4c, 0x71, 0x12, 0xb8, 0x64, 0x8a, 0x83, 0xe9, - 0x18, 0xa1, 0xe0, 0xda, 0x9a, 0xd5, 0x98, 0xd2, 0xcd, 0x66, 0xc7, 0x21, 0xca, 0xff, 0x21, 0x8f, - 0x91, 0x08, 0x9a, 0x8e, 0x91, 0x08, 0x88, 0x2e, 0xd0, 0x14, 0x54, 0x72, 0x5d, 0x73, 0xd9, 0xe2, - 0xfb, 0xca, 0x4e, 0xd3, 0x53, 0xfe, 0x4f, 0x79, 0x81, 0x4e, 0xa2, 0xa1, 0x0b, 0x74, 0x12, 0x1c, - 0x4e, 0x9d, 0x12, 0x12, 0xdc, 0x2b, 0xff, 0x57, 0xe4, 0xd4, 0x29, 0x81, 0x06, 0x4e, 0x9d, 0x92, - 0x92, 0xe3, 0x4f, 0xa1, 0x51, 0x66, 0x93, 0xcd, 0x98, 0xc1, 0x5d, 0xf5, 0xff, 0x2d, 0xaf, 0x8f, - 0x51, 0x3c, 0x5d, 0x1f, 0xa3, 0x30, 0x99, 0x0f, 0xef, 0x82, 0xff, 0x27, 0x8d, 0x4f, 0x20, 0xff, - 0x58, 0x19, 0x7c, 0x5d, 0xe4, 0xc3, 0x47, 0xca, 0xb7, 0x66, 0xd2, 0x18, 0x05, 0xc3, 0x23, 0x56, - 0x48, 0x66, 0xa4, 0x91, 0xbb, 0x26, 0x59, 0x55, 0x3e, 0x9e, 0xca, 0x88, 0x11, 0xc8, 0x8c, 0x18, - 0x0c, 0xbf, 0x8e, 0x4e, 0x86, 0xb0, 0x59, 0xd2, 0x5a, 0x0c, 0x66, 0xa6, 0x6f, 0xcb, 0xc8, 0x66, - 0x70, 0x32, 0x19, 0x35, 0x83, 0x93, 0x31, 0x49, 0xac, 0xb9, 0xe8, 0xfe, 0xdf, 0x4d, 0x58, 0x07, - 0x12, 0x4c, 0x61, 0x90, 0xc4, 0x9a, 0x4b, 0xf3, 0xdb, 0x37, 0x61, 0x1d, 0xc8, 0x34, 0x85, 0x01, - 0xfe, 0x64, 0x06, 0x5d, 0x48, 0x46, 0x95, 0x9a, 0xcd, 0x29, 0xdb, 0x09, 0x71, 0xca, 0x77, 0x64, - 0xe4, 0x83, 0x86, 0xad, 0x15, 0x9b, 0xee, 0xd1, 0xb6, 0x58, 0x01, 0x7e, 0x3f, 0x1a, 0x2e, 0x75, - 0x0c, 0xd3, 0x83, 0x8b, 0x37, 0x6a, 0x38, 0x7f, 0x67, 0x26, 0xb2, 0xc5, 0x11, 0xb1, 0xb0, 0xc5, - 0x11, 0x01, 0xf8, 0x55, 0x34, 0x56, 0x23, 0x8d, 0x8e, 0x63, 0x7a, 0x6b, 0x1a, 0x69, 0xdb, 0x8e, - 0x47, 0x79, 0x7c, 0x57, 0x46, 0x9e, 0xc4, 0x62, 0x14, 0x74, 0x12, 0x8b, 0x01, 0xf1, 0xad, 0x94, - 0x14, 0xf7, 0xca, 0x27, 0x32, 0x5d, 0xaf, 0xe5, 0x83, 0xbe, 0x4c, 0xc9, 0x90, 0x5f, 0x4d, 0x4c, - 0x19, 0xae, 0x7c, 0x32, 0xd3, 0xe5, 0x1a, 0x5d, 0x98, 0xe1, 0x12, 0xb2, 0x8d, 0x57, 0x13, 0x93, - 0x42, 0x2b, 0xdf, 0x9d, 0xe9, 0x72, 0xed, 0x1d, 0x72, 0x4c, 0xca, 0x27, 0xfd, 0x34, 0xf3, 0x14, - 0xe1, 0x8c, 0xbe, 0x27, 0x13, 0x77, 0x15, 0x09, 0xca, 0x0b, 0x84, 0xb4, 0xd8, 0x4d, 0x37, 0x50, - 0xfa, 0x4f, 0x65, 0xe2, 0xbe, 0x79, 0x61, 0xb1, 0xf0, 0x17, 0x26, 0xe8, 0xcc, 0xe4, 0x3d, 0x8f, - 0x38, 0x96, 0xde, 0x84, 0xee, 0xac, 0x79, 0xb6, 0xa3, 0x2f, 0x93, 0x49, 0x4b, 0x5f, 0x6c, 0x12, - 0xe5, 0xd3, 0x19, 0xd9, 0x82, 0x4d, 0x27, 0xa5, 0x16, 0x6c, 0x3a, 0x16, 0xaf, 0xa0, 0xfb, 0x93, - 0xb0, 0x65, 0xd3, 0x85, 0x7a, 0x3e, 0x93, 0x91, 0x4d, 0xd8, 0x2e, 0xb4, 0xd4, 0x84, 0xed, 0x82, - 0xc6, 0x57, 0xd1, 0xc0, 0xb8, 0xed, 0x4f, 0xbf, 0xdf, 0x1b, 0x71, 0x86, 0x0c, 0x30, 0xd3, 0x3d, - 0x5a, 0x48, 0xc6, 0xcb, 0xf0, 0x41, 0xfd, 0xd9, 0x78, 0x99, 0xf0, 0xf2, 0x29, 0xf8, 0xc1, 0xcb, - 0x70, 0x71, 0xff, 0x7f, 0xf1, 0x32, 0xe1, 0x1d, 0x57, 0xf0, 0x83, 0xce, 0x24, 0xac, 0xc6, 0xd9, - 0xa9, 0x12, 0xb5, 0xdb, 0x26, 0x56, 0xf4, 0x66, 0x93, 0x58, 0xcb, 0x44, 0xf9, 0x5c, 0x64, 0x26, - 0x49, 0x26, 0xa3, 0x33, 0x49, 0x32, 0x06, 0x7f, 0x18, 0x9d, 0xba, 0xa5, 0x37, 0x4d, 0x23, 0xc4, - 0xf9, 0x79, 0x86, 0x95, 0xef, 0xcb, 0xc8, 0xbb, 0xe9, 0x14, 0x3a, 0xba, 0x9b, 0x4e, 0x41, 0xe1, - 0x59, 0x84, 0x61, 0x19, 0x0d, 0x66, 0x0b, 0xba, 0x3e, 0x2b, 0xff, 0x7f, 0x46, 0xb6, 0x53, 0xe3, - 0x24, 0xd4, 0x4e, 0x8d, 0x43, 0x71, 0x3d, 0x3d, 0x19, 0x84, 0xf2, 0xfd, 0x19, 0xf9, 0xb4, 0x26, - 0x8d, 0x70, 0xba, 0x47, 0x4b, 0xcf, 0x28, 0x71, 0x1d, 0x8d, 0xd6, 0xaa, 0x95, 0xa9, 0xa9, 0xc9, - 0xda, 0xad, 0x4a, 0x19, 0x1e, 0x3a, 0x18, 0xca, 0x0f, 0x44, 0x56, 0xac, 0x28, 0x01, 0x5d, 0xb1, - 0xa2, 0x30, 0x5c, 0x43, 0x27, 0xa8, 0x20, 0xaa, 0x0e, 0x59, 0x22, 0x0e, 0xb1, 0x1a, 0xfe, 0xb0, - 0xfc, 0xa1, 0x8c, 0x6c, 0x28, 0x24, 0x11, 0x51, 0x43, 0x21, 0x09, 0x8e, 0xef, 0xa0, 0xb3, 0xd1, - 0xc3, 0x1c, 0xf1, 0xe5, 0xa8, 0xf2, 0xc3, 0x99, 0x88, 0x3d, 0xdb, 0x85, 0x18, 0xec, 0xd9, 0x2e, - 0x78, 0x6c, 0xa1, 0x73, 0xfc, 0x64, 0x84, 0xfb, 0x4c, 0x46, 0x6b, 0xfb, 0x11, 0x56, 0xdb, 0x23, - 0xa1, 0x4f, 0x5f, 0x17, 0xea, 0xe9, 0x1e, 0xad, 0x3b, 0x3b, 0xaa, 0x2a, 0xf1, 0xac, 0x05, 0xca, - 0x8f, 0x66, 0x92, 0x9d, 0x4a, 0x24, 0x4f, 0xe3, 0xa4, 0x74, 0x07, 0xaf, 0xa7, 0xc5, 0xdc, 0x57, - 0x7e, 0x2c, 0x32, 0x64, 0x92, 0xc9, 0xe8, 0x90, 0x49, 0x09, 0xda, 0xff, 0x2a, 0x1a, 0x63, 0x7a, - 0x59, 0xd5, 0x61, 0x24, 0x59, 0xcb, 0xc4, 0x50, 0xfe, 0x46, 0x64, 0xc1, 0x8a, 0x51, 0x80, 0x77, - 0x4e, 0x14, 0x48, 0x67, 0xdf, 0x5a, 0x5b, 0xb7, 0x2c, 0x38, 0x29, 0x55, 0xfe, 0x66, 0x64, 0xf6, - 0x0d, 0x51, 0xe0, 0x7b, 0x1b, 0xfc, 0x82, 0xdc, 0x69, 0x49, 0x0f, 0xab, 0x95, 0xcf, 0x47, 0xd6, - 0xb9, 0x44, 0x2a, 0xba, 0xce, 0x25, 0xbf, 0xcb, 0xbe, 0x95, 0xf2, 0x28, 0x59, 0xf9, 0xf1, 0xee, - 0x7c, 0xc3, 0xf5, 0x33, 0xf9, 0x4d, 0xf3, 0xad, 0x94, 0x07, 0xbd, 0xca, 0x4f, 0x74, 0xe7, 0x1b, - 0xba, 0xcb, 0x25, 0xbf, 0x07, 0xae, 0xa7, 0x3f, 0x86, 0x55, 0x7e, 0x32, 0x3a, 0x21, 0xa4, 0x10, - 0xc2, 0x84, 0x90, 0xf6, 0xa2, 0x76, 0x11, 0x9d, 0x66, 0x9d, 0x76, 0xdd, 0xd1, 0xdb, 0x2b, 0x35, - 0xe2, 0x79, 0xa6, 0xb5, 0xec, 0xef, 0x6f, 0xfe, 0x76, 0x26, 0x72, 0xe8, 0x94, 0x46, 0x09, 0x87, - 0x4e, 0x69, 0x48, 0xaa, 0x4f, 0xb1, 0x67, 0xaf, 0xca, 0x4f, 0x45, 0xf4, 0x29, 0x46, 0x41, 0xf5, - 0x29, 0xfe, 0x5a, 0xf6, 0xd5, 0x84, 0xd7, 0x9d, 0xca, 0xdf, 0x49, 0xe7, 0x15, 0xb4, 0x2f, 0xe1, - 0x51, 0xe8, 0xab, 0x09, 0x8f, 0x18, 0x95, 0xbf, 0x9b, 0xce, 0x2b, 0xf4, 0xec, 0x89, 0xbf, 0x7d, - 0x7c, 0x1d, 0x9d, 0x64, 0x73, 0xe4, 0x14, 0x31, 0x88, 0xf4, 0xa1, 0x3f, 0x1d, 0x19, 0x8e, 0xc9, - 0x64, 0x70, 0x90, 0x9d, 0x88, 0x49, 0x62, 0xcd, 0xdb, 0xfa, 0x33, 0x9b, 0xb0, 0x0e, 0xcd, 0xec, - 0x64, 0x0c, 0x7e, 0x41, 0x7e, 0x52, 0xa6, 0xfc, 0x6c, 0x26, 0xe2, 0x24, 0x21, 0x20, 0xc1, 0x49, - 0x42, 0x7c, 0x7f, 0xf6, 0x82, 0xfc, 0x7c, 0x4a, 0xf9, 0x7b, 0x89, 0x85, 0x83, 0x0e, 0x90, 0xdf, - 0x5a, 0xbd, 0x20, 0x3f, 0x15, 0x52, 0x7e, 0x2e, 0xb1, 0x70, 0xf0, 0x01, 0xf2, 0xbb, 0x22, 0xba, - 0xf1, 0xe8, 0x78, 0x36, 0x63, 0x25, 0x4d, 0x0f, 0x3f, 0x1f, 0xdd, 0x78, 0x24, 0x92, 0xc1, 0xc6, - 0x23, 0x11, 0x93, 0xc4, 0x9a, 0x7f, 0xde, 0x2f, 0x6c, 0xc2, 0x5a, 0xd8, 0x2e, 0x25, 0x62, 0x92, - 0x58, 0xf3, 0x8f, 0xff, 0xc2, 0x26, 0xac, 0x85, 0xed, 0x52, 0x22, 0x86, 0x1a, 0x39, 0x21, 0xe6, - 0x16, 0x71, 0xdc, 0x50, 0xfd, 0x7e, 0x31, 0x62, 0xe4, 0xa4, 0xd0, 0x51, 0x23, 0x27, 0x05, 0x95, - 0xc8, 0x9d, 0x0b, 0xe5, 0x97, 0x36, 0xe3, 0x1e, 0xde, 0x76, 0xa4, 0xa0, 0x12, 0xb9, 0x73, 0xb9, - 0xfc, 0xf2, 0x66, 0xdc, 0xc3, 0xeb, 0x8e, 0x14, 0x14, 0x35, 0x78, 0x26, 0x1c, 0x7b, 0xd5, 0x7a, - 0x95, 0xac, 0x92, 0x26, 0x17, 0xc9, 0x17, 0x23, 0x06, 0x4f, 0x94, 0x00, 0x0e, 0xe7, 0x23, 0x30, - 0x99, 0x11, 0xff, 0xfa, 0x5f, 0x4d, 0x65, 0x14, 0x9e, 0x3e, 0x44, 0x61, 0x32, 0x23, 0xfe, 0xa1, - 0xbf, 0x96, 0xca, 0x28, 0x3c, 0x7d, 0x88, 0xc2, 0xc6, 0xfb, 0x50, 0x2f, 0x1c, 0x4f, 0xa9, 0x9f, - 0xcf, 0xa0, 0xa1, 0x9a, 0xe7, 0x10, 0xbd, 0xc5, 0x83, 0x50, 0x9d, 0x41, 0xfd, 0xcc, 0xcf, 0xd3, - 0x7f, 0x94, 0xaa, 0x05, 0xbf, 0xf1, 0x05, 0x34, 0x32, 0xa3, 0xbb, 0x1e, 0x94, 0xac, 0x58, 0x06, - 0xb9, 0x07, 0x6f, 0x9c, 0x72, 0x5a, 0x04, 0x8a, 0x67, 0x18, 0x1d, 0x2b, 0x07, 0xd1, 0xff, 0x72, - 0x9b, 0xc6, 0x5e, 0xea, 0x7f, 0x6b, 0xbd, 0xd8, 0x03, 0xa1, 0x96, 0x22, 0x65, 0xd5, 0xaf, 0x66, - 0x50, 0xcc, 0x03, 0x75, 0xe7, 0x8f, 0xc5, 0xe7, 0xd1, 0xb1, 0x48, 0xc4, 0x49, 0xfe, 0x50, 0x6b, - 0x8b, 0x01, 0x29, 0xa3, 0xa5, 0xf1, 0x7b, 0x82, 0x07, 0x42, 0x37, 0xb5, 0x19, 0x1e, 0x57, 0xab, - 0x6f, 0x63, 0xbd, 0x98, 0xeb, 0x38, 0x4d, 0x4d, 0x40, 0xf1, 0xb8, 0x2f, 0x7f, 0x7f, 0x34, 0x0c, - 0xa7, 0x87, 0x2f, 0xf0, 0x97, 0xeb, 0x99, 0x30, 0x1a, 0x57, 0x24, 0x2f, 0x2f, 0x7b, 0xa9, 0xfe, - 0x7e, 0x34, 0x54, 0x69, 0xb5, 0x89, 0xe3, 0xda, 0x96, 0xee, 0xd9, 0x0e, 0x7f, 0xf8, 0x0b, 0x91, - 0x9a, 0x4c, 0x01, 0x2e, 0x46, 0x0f, 0x12, 0xe9, 0xf1, 0x25, 0x3f, 0x0d, 0x53, 0x0e, 0x02, 0x19, - 0xc2, 0xeb, 0xbd, 0x68, 0xae, 0x57, 0x46, 0x41, 0x49, 0x6f, 0xba, 0x3a, 0x3c, 0x25, 0x0b, 0x48, - 0x3b, 0x14, 0x20, 0x92, 0x02, 0x05, 0x7e, 0x0c, 0x15, 0xe0, 0xe8, 0xdd, 0x85, 0xf4, 0x6a, 0x3c, - 0x46, 0x58, 0x13, 0x20, 0x62, 0x44, 0x26, 0x46, 0x83, 0x6f, 0xa0, 0xd1, 0xf0, 0x5e, 0xf1, 0xba, - 0x63, 0x77, 0xda, 0x7e, 0x42, 0x05, 0xc8, 0x91, 0x7b, 0x27, 0xc0, 0xd5, 0x97, 0x01, 0x29, 0xb0, - 0x88, 0x15, 0xc4, 0xd3, 0xe8, 0x58, 0x08, 0xa3, 0x22, 0xf2, 0x13, 0xb9, 0x40, 0x8e, 0x68, 0x81, - 0x17, 0x15, 0xa7, 0xc8, 0x2a, 0x5a, 0x0c, 0x57, 0x50, 0x9f, 0x1f, 0x20, 0xac, 0x7f, 0x53, 0x25, - 0x3d, 0xce, 0x03, 0x84, 0xf5, 0x89, 0xa1, 0xc1, 0xfc, 0xf2, 0x78, 0x0a, 0x8d, 0x68, 0x76, 0xc7, - 0x23, 0x0b, 0x36, 0xb7, 0xe6, 0x79, 0xa4, 0x7f, 0x68, 0x93, 0x43, 0x31, 0x75, 0xcf, 0xf6, 0x53, - 0x0c, 0x8b, 0xa9, 0x6e, 0xe5, 0x52, 0x78, 0x0e, 0x8d, 0xc5, 0x6e, 0x60, 0xc5, 0xc4, 0xbf, 0xc2, - 0xe7, 0xc5, 0x99, 0xc5, 0x8b, 0xe2, 0xef, 0xcc, 0xa0, 0xc2, 0x82, 0xa3, 0x9b, 0x9e, 0xcb, 0x5f, - 0xa1, 0xdd, 0x77, 0x79, 0xd5, 0xd1, 0xdb, 0x54, 0x3f, 0x2e, 0x43, 0xa4, 0xca, 0x5b, 0x7a, 0xb3, - 0x43, 0xdc, 0xf1, 0xdb, 0xf4, 0xeb, 0xfe, 0xf5, 0x7a, 0xf1, 0x85, 0x65, 0x38, 0xe7, 0xb9, 0xdc, - 0xb0, 0x5b, 0x57, 0x96, 0x1d, 0xfd, 0xae, 0xe9, 0xc1, 0xba, 0xaf, 0x37, 0xaf, 0x78, 0xa4, 0x09, - 0xc7, 0x49, 0x57, 0xf4, 0xb6, 0x79, 0x05, 0x22, 0x22, 0x5f, 0x09, 0x38, 0xb1, 0x1a, 0xa8, 0x0a, - 0x78, 0xf0, 0x97, 0xa8, 0x02, 0x0c, 0x87, 0xe7, 0x10, 0xe2, 0x9f, 0x5a, 0x6a, 0xb7, 0xf9, 0x93, - 0x36, 0xe1, 0x10, 0xc6, 0xc7, 0x30, 0xc5, 0x0e, 0x04, 0xa6, 0xb7, 0xdb, 0x62, 0x6a, 0xf1, 0x90, - 0x8e, 0x6a, 0xc1, 0x02, 0x6f, 0x91, 0x2f, 0xa6, 0xe1, 0x50, 0xe2, 0x7e, 0x63, 0x13, 0x84, 0x14, - 0x2d, 0x86, 0x17, 0xd1, 0x31, 0xce, 0x37, 0x08, 0xbd, 0x3f, 0x22, 0xcf, 0x0a, 0x11, 0x34, 0x53, - 0xda, 0xa0, 0x8d, 0x06, 0x07, 0x8b, 0x75, 0x44, 0x4a, 0xe0, 0xf1, 0x30, 0xeb, 0xe4, 0x9c, 0xde, - 0x22, 0xae, 0x72, 0x0c, 0x34, 0xf6, 0xec, 0xc6, 0x7a, 0x51, 0xf1, 0xcb, 0x43, 0xac, 0xbc, 0xc4, - 0x0c, 0xcc, 0x50, 0x44, 0xe4, 0xc1, 0xb4, 0x7e, 0x34, 0x81, 0x47, 0x54, 0xe7, 0xe5, 0x22, 0x78, - 0x02, 0x0d, 0x07, 0x1e, 0xf5, 0x37, 0x6f, 0x56, 0xca, 0xf0, 0x66, 0x6e, 0x60, 0xfc, 0xdc, 0xc6, - 0x7a, 0xf1, 0x74, 0x24, 0xaa, 0xbf, 0xc8, 0x44, 0x2a, 0x23, 0x84, 0x21, 0x60, 0x8f, 0xe8, 0x22, - 0x61, 0x08, 0xda, 0x09, 0x61, 0x08, 0xaa, 0xf8, 0x25, 0x34, 0x58, 0xba, 0x5d, 0xe3, 0xe1, 0x15, - 0x5c, 0xe5, 0x78, 0x98, 0x4e, 0x05, 0x92, 0x70, 0xf3, 0x50, 0x0c, 0x62, 0xd3, 0x45, 0x7a, 0x3c, - 0x89, 0x46, 0x24, 0xa7, 0x1c, 0x57, 0x39, 0x01, 0x1c, 0xa0, 0xe5, 0x3a, 0x60, 0xea, 0x0e, 0x47, - 0x49, 0x69, 0xe1, 0xa5, 0x42, 0x54, 0x6b, 0xca, 0xa6, 0x0b, 0xa9, 0x29, 0x34, 0x02, 0x91, 0x1c, - 0xe0, 0x05, 0x5e, 0x3f, 0xd3, 0x1a, 0x83, 0xa3, 0xea, 0x0e, 0xc3, 0x49, 0x79, 0xe0, 0xe5, 0x62, - 0xf8, 0x0d, 0x84, 0x21, 0x99, 0x05, 0x31, 0xfc, 0x3b, 0x9a, 0x4a, 0xd9, 0x55, 0x4e, 0x42, 0xc4, - 0x5e, 0x1c, 0x7d, 0x39, 0x5e, 0x29, 0x8f, 0x5f, 0xe0, 0xd3, 0xc7, 0x79, 0x9d, 0x95, 0xaa, 0xfb, - 0xaf, 0xc6, 0xeb, 0xa6, 0x21, 0xb6, 0x38, 0x81, 0x2b, 0x5e, 0x45, 0xa7, 0xaa, 0x0e, 0xb9, 0x6b, - 0xda, 0x1d, 0xd7, 0x5f, 0x3e, 0xfc, 0x79, 0xeb, 0xd4, 0xa6, 0xf3, 0xd6, 0x83, 0xbc, 0xe2, 0xfb, - 0xda, 0x0e, 0xb9, 0x5b, 0xf7, 0xe3, 0xb4, 0x4a, 0x01, 0x0e, 0xd3, 0xb8, 0x53, 0x71, 0x41, 0x14, - 0x0b, 0x0e, 0x37, 0x89, 0xab, 0x28, 0xe1, 0x54, 0xcb, 0x82, 0x72, 0x98, 0x01, 0x4e, 0x14, 0x57, - 0xa4, 0x18, 0xd6, 0x10, 0xbe, 0x3e, 0xe1, 0xdf, 0xd7, 0x95, 0x1a, 0x2c, 0xab, 0xa3, 0x72, 0x1a, - 0x98, 0xa9, 0x54, 0x2c, 0xcb, 0x8d, 0x20, 0x66, 0x73, 0x5d, 0xe7, 0x78, 0x51, 0x2c, 0xf1, 0xd2, - 0x78, 0x06, 0x8d, 0x56, 0x1d, 0x38, 0x7a, 0xb8, 0x41, 0xd6, 0xaa, 0x76, 0xd3, 0x6c, 0xac, 0xc1, - 0x43, 0x40, 0x3e, 0x55, 0xb6, 0x19, 0xae, 0x7e, 0x87, 0xac, 0xd5, 0xdb, 0x80, 0x15, 0x97, 0x95, - 0x68, 0x49, 0x31, 0x86, 0xea, 0xfd, 0x5b, 0x8b, 0xa1, 0x4a, 0xd0, 0x28, 0xbf, 0xed, 0xbb, 0xe7, - 0x11, 0x8b, 0x2e, 0xf5, 0x2e, 0x7f, 0xf4, 0xa7, 0x44, 0x6e, 0x07, 0x03, 0x3c, 0xcf, 0x09, 0xcf, - 0x46, 0x19, 0x09, 0xc0, 0x62, 0xc3, 0xa2, 0x45, 0xd4, 0xcf, 0xe4, 0xc4, 0xa9, 0x13, 0x9f, 0x45, - 0x79, 0x21, 0x85, 0x07, 0x84, 0x5e, 0x84, 0x70, 0xc7, 0x79, 0x1e, 0xd7, 0x75, 0x80, 0x9b, 0x1d, - 0x41, 0x8c, 0x10, 0xc8, 0x6f, 0xe6, 0xc7, 0x55, 0x36, 0x0d, 0x2d, 0x24, 0x80, 0xdc, 0x52, 0x9d, - 0xc5, 0xa6, 0xd9, 0x80, 0x20, 0xd8, 0x39, 0x21, 0x28, 0x00, 0x40, 0x59, 0x0c, 0x6c, 0x81, 0x04, - 0x5f, 0x45, 0x83, 0xfe, 0x69, 0x55, 0x18, 0x7a, 0x14, 0x62, 0x23, 0xfb, 0xd9, 0xf3, 0x59, 0xe8, - 0x65, 0x81, 0x08, 0x3f, 0x8f, 0x50, 0x38, 0x92, 0xb9, 0x91, 0x04, 0xb3, 0xbc, 0x38, 0xf0, 0xc5, - 0x59, 0x3e, 0xa4, 0xa6, 0x73, 0x9e, 0xa8, 0x49, 0x7e, 0x36, 0x3d, 0x98, 0xf3, 0x24, 0xf5, 0x13, - 0xfb, 0x56, 0x2e, 0x82, 0xe7, 0xd1, 0x58, 0x4c, 0x79, 0x78, 0xa0, 0x52, 0xc8, 0xdb, 0x9b, 0xa0, - 0x79, 0xe2, 0x9a, 0x1a, 0x2b, 0xab, 0x7e, 0x5b, 0x36, 0xb6, 0x62, 0x50, 0xc1, 0x70, 0x2a, 0xa1, - 0x73, 0x40, 0x30, 0x3e, 0x6b, 0x26, 0x18, 0x81, 0x08, 0x5f, 0x44, 0xfd, 0x55, 0x3a, 0x5e, 0x1b, - 0x76, 0x93, 0x77, 0x15, 0x44, 0xe4, 0x69, 0x73, 0x98, 0x16, 0x60, 0xf1, 0x55, 0x21, 0xbf, 0xa3, - 0x10, 0xe0, 0xd7, 0xcf, 0xef, 0x28, 0x4e, 0xb8, 0x41, 0xa6, 0xc7, 0xab, 0x91, 0x54, 0x32, 0xbc, - 0x4c, 0xc2, 0x6a, 0x15, 0x26, 0xca, 0x0a, 0x6c, 0xc5, 0xde, 0xcd, 0x6c, 0x45, 0xf5, 0x37, 0x33, - 0x71, 0xed, 0xc7, 0xd7, 0xe2, 0x51, 0x3e, 0x61, 0x69, 0x08, 0x80, 0x62, 0xad, 0x41, 0xbc, 0x4f, - 0x29, 0x5e, 0x67, 0x76, 0xc7, 0xf1, 0x3a, 0x73, 0xdb, 0x8c, 0xd7, 0xa9, 0xfe, 0x8f, 0x7c, 0x57, - 0x9f, 0xd1, 0x7d, 0x89, 0x4b, 0xf5, 0x1c, 0xdd, 0xef, 0xd0, 0xda, 0x4b, 0x6e, 0xcc, 0x6a, 0x67, - 0x2e, 0x71, 0x75, 0x9d, 0x8d, 0x1a, 0x57, 0x93, 0x29, 0xf1, 0xcb, 0x68, 0xc8, 0xff, 0x00, 0x88, - 0x03, 0x2b, 0xc4, 0x2f, 0x0d, 0xd6, 0x9a, 0x48, 0xc4, 0x54, 0xa9, 0x00, 0x7e, 0x1a, 0x0d, 0x80, - 0xa5, 0xd1, 0xd6, 0x1b, 0x7e, 0x90, 0x60, 0x16, 0x55, 0xd8, 0x07, 0x8a, 0xb1, 0x97, 0x02, 0x4a, - 0xfc, 0x11, 0x54, 0xe0, 0xf1, 0xea, 0x59, 0xea, 0xe3, 0x2b, 0x5b, 0x70, 0xb2, 0xbd, 0x2c, 0xc6, - 0xaa, 0x67, 0x7b, 0x07, 0x00, 0x48, 0x7b, 0x07, 0x16, 0xa6, 0x7e, 0x01, 0x1d, 0xaf, 0x3a, 0xc4, - 0x00, 0x77, 0xee, 0xc9, 0x7b, 0x6d, 0x87, 0x67, 0x12, 0x60, 0x03, 0x18, 0x96, 0x8e, 0xb6, 0x8f, - 0xa6, 0x8b, 0x1a, 0xc7, 0x0b, 0x8c, 0x92, 0x8a, 0x53, 0x7b, 0x82, 0xb5, 0xe4, 0x06, 0x59, 0x5b, - 0xb5, 0x1d, 0x83, 0x05, 0xdb, 0xe7, 0xf6, 0x04, 0x17, 0xf4, 0x1d, 0x8e, 0x12, 0xed, 0x09, 0xb9, - 0xd0, 0x99, 0xe7, 0xd0, 0xe0, 0x4e, 0xe3, 0xbd, 0xff, 0x52, 0x36, 0xe5, 0xf5, 0xc5, 0xe1, 0xcd, - 0xd3, 0x17, 0xe4, 0x4c, 0xed, 0x4d, 0xc9, 0x99, 0xfa, 0x8d, 0x6c, 0xca, 0xd3, 0x92, 0x43, 0x9d, - 0xdb, 0x30, 0x10, 0x86, 0x9c, 0xdb, 0x30, 0x4c, 0x2b, 0x69, 0x1a, 0x9a, 0x48, 0x14, 0xc9, 0x82, - 0x5a, 0xd8, 0x34, 0x0b, 0xea, 0x4f, 0xe7, 0xba, 0x3d, 0xbd, 0x39, 0x92, 0xfd, 0x76, 0x64, 0x7f, - 0x15, 0x0d, 0x06, 0x92, 0xad, 0x94, 0xc1, 0x9e, 0x19, 0x0e, 0xb2, 0x4b, 0x30, 0x30, 0x94, 0x11, - 0x88, 0xf0, 0x25, 0xd6, 0xd6, 0x9a, 0xf9, 0x26, 0x8b, 0xb0, 0x3e, 0xcc, 0x63, 0x67, 0xeb, 0x9e, - 0x5e, 0x77, 0xcd, 0x37, 0x89, 0x16, 0xa0, 0xd5, 0x7f, 0x94, 0x4d, 0x7c, 0xbf, 0x74, 0xd4, 0x47, - 0xdb, 0xe8, 0xa3, 0x04, 0x21, 0xb2, 0x97, 0x57, 0x47, 0x42, 0xdc, 0x86, 0x10, 0xff, 0x3c, 0x9b, - 0xf8, 0x4e, 0xed, 0x48, 0x88, 0xdb, 0x99, 0x2d, 0x1e, 0x43, 0x03, 0x9a, 0xbd, 0xea, 0x4e, 0xc0, - 0x9e, 0x85, 0xcd, 0x15, 0x30, 0x51, 0x3b, 0xf6, 0xaa, 0x5b, 0x87, 0xdd, 0x88, 0x16, 0x12, 0xa8, - 0xdf, 0xcc, 0x76, 0x79, 0xc9, 0x77, 0x24, 0xf8, 0xb7, 0x73, 0x89, 0xfc, 0xd5, 0xac, 0xf4, 0x52, - 0xf0, 0x50, 0x27, 0x09, 0xaf, 0x35, 0x56, 0x48, 0x4b, 0x8f, 0x26, 0x09, 0x77, 0x01, 0xca, 0x73, - 0x8c, 0x86, 0x24, 0xea, 0x97, 0xb2, 0x91, 0xa7, 0x92, 0x47, 0xb2, 0xdb, 0xb2, 0xec, 0x02, 0xad, - 0xe3, 0xaf, 0x3f, 0x8f, 0x24, 0xb7, 0x55, 0xc9, 0x7d, 0x22, 0x1b, 0x79, 0x28, 0x7b, 0x78, 0xf3, - 0x05, 0x7f, 0x29, 0x1b, 0x7f, 0xf4, 0x7b, 0x78, 0x35, 0xe9, 0x31, 0x34, 0xc0, 0xe5, 0x10, 0x2c, - 0x15, 0x6c, 0xde, 0x67, 0x40, 0x38, 0x40, 0x0d, 0x08, 0xd4, 0xef, 0xc8, 0x22, 0xf9, 0x01, 0xf3, - 0x21, 0xd5, 0xa1, 0x5f, 0xcd, 0xca, 0x4f, 0xb7, 0x0f, 0xaf, 0xfe, 0x5c, 0x46, 0xa8, 0xd6, 0x59, - 0x6c, 0xf0, 0xc8, 0x9f, 0xbd, 0xc2, 0x09, 0x7c, 0x00, 0xd5, 0x04, 0x0a, 0xf5, 0x7f, 0x66, 0x13, - 0xdf, 0x93, 0x1f, 0x5e, 0x01, 0x3e, 0x05, 0xa7, 0xe2, 0x0d, 0x2b, 0x9c, 0xc8, 0xe1, 0x10, 0x92, - 0x8e, 0xbf, 0x58, 0x6a, 0x33, 0x9f, 0x10, 0xbf, 0x2f, 0xc1, 0x5c, 0x83, 0xc0, 0xf1, 0xa1, 0xb9, - 0x26, 0xde, 0x30, 0x08, 0x86, 0xdb, 0xef, 0x64, 0x37, 0x7b, 0x7e, 0x7f, 0x98, 0x57, 0xd5, 0xbe, - 0xaa, 0xbe, 0x06, 0x61, 0xe2, 0x68, 0x4f, 0x0c, 0xb1, 0xc4, 0x5b, 0x6d, 0x06, 0x12, 0x6f, 0xc4, - 0x38, 0x95, 0xfa, 0xa7, 0xbd, 0xc9, 0x6f, 0xbf, 0x0f, 0xaf, 0x08, 0xcf, 0xa2, 0x7c, 0x55, 0xf7, - 0x56, 0xb8, 0x26, 0xc3, 0x6d, 0x5d, 0x5b, 0xf7, 0x56, 0x34, 0x80, 0xe2, 0x4b, 0xa8, 0x5f, 0xd3, - 0x57, 0xd9, 0x99, 0x67, 0x21, 0x4c, 0x8a, 0xe6, 0xe8, 0xab, 0x75, 0x76, 0xee, 0x19, 0xa0, 0xb1, - 0x1a, 0x24, 0xe5, 0x63, 0x27, 0xdf, 0x90, 0xd1, 0x8a, 0x25, 0xe5, 0x0b, 0x52, 0xf1, 0x9d, 0x45, - 0xf9, 0x71, 0xdb, 0x58, 0x03, 0x67, 0x96, 0x21, 0x56, 0xd9, 0xa2, 0x6d, 0xac, 0x69, 0x00, 0xc5, - 0x9f, 0xcc, 0xa0, 0xbe, 0x69, 0xa2, 0x1b, 0x74, 0x84, 0x0c, 0x74, 0xf3, 0x05, 0xf9, 0xc0, 0xde, - 0xf8, 0x82, 0x8c, 0xad, 0xb0, 0xca, 0x44, 0x45, 0xe1, 0xf5, 0xe3, 0xeb, 0xa8, 0x7f, 0x42, 0xf7, - 0xc8, 0xb2, 0xed, 0xac, 0x81, 0x77, 0xcb, 0x48, 0xe8, 0xe9, 0x2c, 0xe9, 0x8f, 0x4f, 0xc4, 0x6e, - 0xc6, 0x1a, 0xfc, 0x97, 0x16, 0x14, 0xa6, 0x62, 0xe1, 0xc9, 0xba, 0x07, 0x43, 0xb1, 0xb0, 0xac, - 0xdc, 0x41, 0x4e, 0xee, 0xe0, 0x58, 0x79, 0x28, 0xf9, 0x58, 0x19, 0xac, 0x47, 0xf0, 0x80, 0x83, - 0x54, 0x78, 0xc3, 0xb0, 0xe8, 0x33, 0xeb, 0x11, 0xa0, 0x90, 0x09, 0x4f, 0x13, 0x48, 0xd4, 0xaf, - 0xf5, 0xa2, 0xc4, 0x97, 0xa2, 0x47, 0x4a, 0x7e, 0xa4, 0xe4, 0xa1, 0x92, 0x97, 0x63, 0x4a, 0x7e, - 0x26, 0xfe, 0xf6, 0xf8, 0x1d, 0xaa, 0xe1, 0x3f, 0x98, 0x8f, 0x45, 0x2e, 0x38, 0xdc, 0xbb, 0xcb, - 0x50, 0x7a, 0xbd, 0x9b, 0x4a, 0x2f, 0x18, 0x10, 0x85, 0x4d, 0x07, 0x44, 0xdf, 0x56, 0x07, 0x44, - 0x7f, 0xea, 0x80, 0x08, 0x15, 0x64, 0x20, 0x55, 0x41, 0x2a, 0x7c, 0xd0, 0xa0, 0xee, 0x09, 0x14, - 0xce, 0x6e, 0xac, 0x17, 0x47, 0xe8, 0x68, 0x4a, 0xcc, 0x9c, 0x00, 0x2c, 0xd4, 0xaf, 0xe6, 0xbb, - 0x84, 0x1b, 0xd9, 0x17, 0x1d, 0x79, 0x0a, 0xe5, 0x4a, 0xed, 0x36, 0xd7, 0x8f, 0xe3, 0x42, 0xa4, - 0x93, 0x94, 0x52, 0x94, 0x1a, 0x3f, 0x8f, 0x72, 0xa5, 0xdb, 0xb5, 0x68, 0xd2, 0x84, 0xd2, 0xed, - 0x1a, 0xff, 0x92, 0xd4, 0xb2, 0xb7, 0x6b, 0xf8, 0xc5, 0x30, 0x7a, 0xe1, 0x4a, 0xc7, 0xba, 0xc3, - 0x37, 0x8a, 0xdc, 0x09, 0xd6, 0xf7, 0xb4, 0x69, 0x50, 0x14, 0xdd, 0x2e, 0x46, 0x68, 0x23, 0xda, - 0x54, 0xd8, 0xba, 0x36, 0xf5, 0x6d, 0xaa, 0x4d, 0xfd, 0x5b, 0xd5, 0xa6, 0x81, 0x2d, 0x68, 0x13, - 0xda, 0x54, 0x9b, 0x06, 0x77, 0xaf, 0x4d, 0x6d, 0x74, 0x26, 0x1e, 0x22, 0x2a, 0xd0, 0x08, 0x0d, - 0xe1, 0x38, 0x96, 0x3b, 0x96, 0xc0, 0xd5, 0x7f, 0x87, 0x61, 0xeb, 0x2c, 0xa9, 0x7e, 0x34, 0x25, - 0xbd, 0x96, 0x50, 0x5a, 0xfd, 0xa5, 0x6c, 0x7a, 0x64, 0xab, 0x83, 0x39, 0xc5, 0x7d, 0x4b, 0xa2, - 0x94, 0xf2, 0xf2, 0x4b, 0xe3, 0x74, 0x29, 0x47, 0xd8, 0x26, 0xc9, 0xec, 0x2b, 0x99, 0xb4, 0x70, - 0x5b, 0xbb, 0x92, 0xd8, 0x23, 0x71, 0x67, 0x35, 0xf0, 0x9e, 0x77, 0x65, 0x2f, 0xb5, 0x68, 0x8e, - 0xf6, 0xdc, 0x0e, 0x73, 0xb4, 0xff, 0x66, 0x06, 0x1d, 0xbf, 0xd1, 0x59, 0x24, 0xdc, 0x39, 0x2d, - 0x68, 0xc6, 0x1b, 0x08, 0x51, 0x30, 0x77, 0x62, 0xc9, 0x80, 0x13, 0xcb, 0x7b, 0xc5, 0x50, 0x59, - 0x91, 0x02, 0x97, 0x43, 0x6a, 0xe6, 0xc0, 0x72, 0xce, 0x77, 0xb1, 0xbc, 0xd3, 0x59, 0x24, 0xf5, - 0x98, 0x27, 0x8b, 0xc0, 0xfd, 0xcc, 0x4b, 0xcc, 0x79, 0x7d, 0xa7, 0x4e, 0x23, 0xbf, 0x90, 0x4d, - 0x8d, 0x4e, 0x76, 0x60, 0xd3, 0xe8, 0x7d, 0x28, 0xb1, 0x57, 0xa2, 0xe9, 0xf4, 0x12, 0x48, 0x22, - 0x1c, 0x93, 0xb8, 0x24, 0x0b, 0xec, 0x80, 0x27, 0x77, 0x7c, 0x5b, 0x05, 0xf6, 0xfb, 0x99, 0xd4, - 0x28, 0x72, 0x07, 0x55, 0x60, 0xea, 0x7f, 0xc8, 0xf9, 0xc1, 0xeb, 0x76, 0xf5, 0x09, 0x8f, 0xa1, - 0x01, 0x9e, 0xa1, 0x47, 0xf6, 0xad, 0xe5, 0x47, 0x79, 0x70, 0x34, 0x1c, 0x10, 0xd0, 0x65, 0xde, - 0x0f, 0xae, 0x15, 0x64, 0xf5, 0x87, 0x65, 0xde, 0xe4, 0x50, 0x4a, 0x2f, 0x90, 0xd0, 0x85, 0x7c, - 0xf2, 0x9e, 0xe9, 0x81, 0x55, 0x40, 0xfb, 0x32, 0xc7, 0x16, 0x72, 0x72, 0xcf, 0xf4, 0x98, 0x4d, - 0x10, 0xa0, 0xe9, 0x22, 0x5d, 0x0b, 0x53, 0x57, 0xf3, 0x45, 0xda, 0xe5, 0x19, 0xbc, 0xf9, 0x63, - 0xae, 0xc7, 0xd0, 0x00, 0x77, 0x58, 0xe5, 0x6e, 0x26, 0xbc, 0xb5, 0xdc, 0xc5, 0x15, 0x5a, 0x1b, - 0x10, 0x50, 0x8e, 0x1a, 0x59, 0x0e, 0x1d, 0xeb, 0x80, 0xa3, 0x03, 0x10, 0x8d, 0x63, 0xf0, 0x55, - 0x34, 0x52, 0xf3, 0x74, 0xcb, 0xd0, 0x1d, 0x63, 0xbe, 0xe3, 0xb5, 0x3b, 0x9e, 0x68, 0x94, 0xba, - 0x9e, 0x61, 0x77, 0x3c, 0x2d, 0x42, 0x81, 0x9f, 0x40, 0xc3, 0x3e, 0x64, 0xd2, 0x71, 0x6c, 0x47, - 0xb4, 0x3c, 0x5c, 0xcf, 0x20, 0x8e, 0xa3, 0xc9, 0x04, 0xf8, 0x7d, 0x68, 0xb8, 0x62, 0xdd, 0xb5, - 0x1b, 0xec, 0xc5, 0xad, 0x36, 0xc3, 0xed, 0x10, 0x78, 0x20, 0x65, 0x06, 0x88, 0x7a, 0xc7, 0x69, - 0x6a, 0x32, 0xa1, 0xba, 0x91, 0x8d, 0xc7, 0xf8, 0x3b, 0xbc, 0x9b, 0x96, 0x4b, 0xb2, 0x33, 0x1d, - 0x78, 0x90, 0x82, 0x41, 0x28, 0xfa, 0xf2, 0x32, 0xbb, 0xf0, 0x2a, 0xea, 0xbf, 0x41, 0xd6, 0x98, - 0xdf, 0x67, 0x21, 0x74, 0x15, 0xbe, 0xc3, 0x61, 0xe2, 0x89, 0xab, 0x4f, 0xa7, 0x7e, 0x39, 0x1b, - 0x8f, 0x5e, 0x78, 0x78, 0x85, 0xfd, 0x04, 0xea, 0x03, 0x51, 0x56, 0xfc, 0x23, 0x7f, 0x10, 0x20, - 0x88, 0x5b, 0xf6, 0x40, 0xf6, 0xc9, 0xd4, 0x1f, 0x2f, 0x44, 0x43, 0x5a, 0x1e, 0x5e, 0xe9, 0xbd, - 0x80, 0x06, 0x27, 0x6c, 0xcb, 0x35, 0x5d, 0x8f, 0x58, 0x0d, 0x5f, 0x61, 0x4f, 0x53, 0x83, 0xaa, - 0x11, 0x82, 0xc5, 0x97, 0x41, 0x02, 0xf5, 0x4e, 0x94, 0x17, 0x3f, 0x83, 0x06, 0x40, 0xe4, 0xe0, - 0x27, 0xcd, 0x26, 0x3c, 0xb8, 0x2d, 0x58, 0xa4, 0xc0, 0xa8, 0x93, 0x74, 0x48, 0x8a, 0x6f, 0xa2, - 0xfe, 0x89, 0x15, 0xb3, 0x69, 0x38, 0xc4, 0x02, 0x7f, 0x61, 0x21, 0xc6, 0x81, 0xdc, 0x97, 0x97, - 0xe1, 0x5f, 0xa0, 0x65, 0xcd, 0x69, 0xf0, 0x62, 0xd2, 0xdb, 0x28, 0x0e, 0x3b, 0xf3, 0xfd, 0x59, - 0x84, 0xc2, 0x02, 0xf8, 0x01, 0x94, 0x0d, 0xb2, 0xc2, 0x82, 0x9b, 0x8a, 0xa4, 0x41, 0x59, 0x58, - 0x2a, 0xf8, 0xd8, 0xce, 0x6e, 0x3a, 0xb6, 0x6f, 0xa2, 0x02, 0x3b, 0xf1, 0x02, 0x4f, 0x72, 0x21, - 0xca, 0x5e, 0x6a, 0x83, 0x2f, 0x03, 0x3d, 0xdb, 0xcc, 0x82, 0xe5, 0x29, 0x79, 0x65, 0x33, 0x66, - 0x67, 0x1a, 0xa8, 0x17, 0xfe, 0xc2, 0x17, 0x50, 0x7e, 0xc1, 0xcf, 0x28, 0x39, 0xcc, 0x66, 0xe9, - 0x88, 0xfc, 0x00, 0x4f, 0xbb, 0x69, 0xc2, 0xb6, 0x3c, 0x5a, 0x35, 0xb4, 0x7a, 0x88, 0xcb, 0x85, - 0xc3, 0x24, 0xb9, 0x70, 0x98, 0xfa, 0x4f, 0xb3, 0x09, 0xc1, 0x56, 0x0f, 0xef, 0x30, 0x79, 0x0e, - 0x21, 0x78, 0x68, 0x4d, 0xe5, 0xe9, 0x3f, 0xd1, 0x80, 0x51, 0x02, 0x8c, 0x40, 0x6d, 0xa5, 0x6d, - 0x47, 0x48, 0xac, 0xfe, 0x76, 0x26, 0x16, 0xa1, 0x73, 0x57, 0x72, 0x14, 0xad, 0xb2, 0xec, 0x0e, - 0xcd, 0x58, 0xbf, 0x2f, 0x72, 0xdb, 0xeb, 0x0b, 0xf9, 0x5b, 0xf6, 0xc0, 0x32, 0xdd, 0xcf, 0x6f, - 0xf9, 0x5a, 0x36, 0x29, 0x5e, 0xe9, 0xc1, 0x54, 0xf1, 0x6b, 0x81, 0x51, 0x9a, 0x8f, 0x44, 0x88, - 0x06, 0x68, 0x34, 0xeb, 0x2d, 0x37, 0x53, 0x3f, 0x8a, 0x8e, 0x45, 0xa2, 0x78, 0xf2, 0x04, 0xa4, - 0x17, 0xba, 0x87, 0x03, 0x4d, 0x7f, 0xa2, 0x2f, 0x91, 0xa9, 0xff, 0x2b, 0xd3, 0x3d, 0x86, 0xeb, - 0xbe, 0xab, 0x4e, 0x82, 0x00, 0x72, 0x7f, 0x3d, 0x02, 0xd8, 0x83, 0x6d, 0xf0, 0xc1, 0x16, 0xc0, - 0x3b, 0x64, 0xf2, 0x78, 0xbb, 0x05, 0xf0, 0xe3, 0x99, 0x4d, 0x43, 0xf0, 0xee, 0xb7, 0x0c, 0xd4, - 0x7f, 0x9b, 0x49, 0x0c, 0x95, 0xbb, 0xab, 0x76, 0xbd, 0x88, 0x0a, 0xcc, 0xad, 0x86, 0xb7, 0x4a, - 0x48, 0x2e, 0x44, 0xa1, 0x69, 0xe9, 0xb8, 0x19, 0x16, 0xcf, 0xa0, 0x3e, 0xd6, 0x06, 0x83, 0xf7, - 0xc6, 0xc3, 0x5d, 0xe2, 0xf5, 0x1a, 0x69, 0x93, 0x23, 0x47, 0xab, 0xbf, 0x95, 0x89, 0x45, 0xee, - 0xdd, 0xc7, 0x6f, 0x0b, 0xa7, 0xea, 0xdc, 0xd6, 0xa7, 0x6a, 0xf5, 0x4f, 0xb2, 0xc9, 0x81, 0x83, - 0xf7, 0xf1, 0x43, 0xf6, 0xe2, 0x38, 0x6d, 0x67, 0xeb, 0xd6, 0x02, 0x1a, 0x91, 0x65, 0xc1, 0x97, - 0xad, 0xf3, 0xc9, 0xe1, 0x93, 0x53, 0x5a, 0x11, 0xe1, 0xa1, 0xbe, 0x95, 0x89, 0xc7, 0x3c, 0xde, - 0xf7, 0xf9, 0x69, 0x67, 0xda, 0x22, 0x7f, 0xca, 0x3b, 0x64, 0xad, 0xd9, 0x8b, 0x4f, 0x79, 0x87, - 0xac, 0x1a, 0x3b, 0xfb, 0x94, 0x9f, 0xcd, 0xa6, 0x85, 0x8c, 0xde, 0xf7, 0x0f, 0xfa, 0xa0, 0x28, - 0x64, 0xd6, 0x32, 0xfe, 0x69, 0x0f, 0xa4, 0xc5, 0x68, 0x4e, 0xe1, 0x19, 0xe3, 0xb3, 0xb3, 0x31, - 0x9e, 0x28, 0xac, 0x77, 0x88, 0x22, 0x1f, 0x0c, 0x61, 0xbd, 0x43, 0x86, 0xca, 0x3b, 0x4f, 0x58, - 0xbf, 0x9e, 0xdd, 0x6a, 0x9c, 0xf2, 0x23, 0xe1, 0xc5, 0x84, 0xf7, 0xd9, 0x6c, 0x3c, 0x7e, 0xfe, - 0xbe, 0x8b, 0x69, 0x0a, 0x15, 0x78, 0x24, 0xff, 0x54, 0xe1, 0x30, 0x7c, 0x9a, 0x45, 0xc3, 0xbf, - 0xe3, 0x1a, 0xe2, 0x17, 0x39, 0x5b, 0x13, 0x09, 0xa3, 0x55, 0xbf, 0x99, 0x89, 0x04, 0x9b, 0xdf, - 0x97, 0x23, 0x84, 0x1d, 0x2d, 0x49, 0xf8, 0x25, 0xff, 0x30, 0x33, 0x1f, 0x89, 0x14, 0x1c, 0x7c, - 0x4f, 0x99, 0x78, 0xba, 0xd9, 0x8c, 0x96, 0xe7, 0x31, 0x01, 0xbe, 0x9c, 0x45, 0x63, 0x31, 0x52, - 0x7c, 0x41, 0x8a, 0x92, 0x03, 0xc7, 0x92, 0x11, 0xe7, 0x71, 0x16, 0x2f, 0x67, 0x1b, 0x27, 0xa9, - 0x17, 0x50, 0xbe, 0xac, 0xaf, 0xb1, 0x6f, 0xeb, 0x65, 0x2c, 0x0d, 0x7d, 0x4d, 0x3c, 0x71, 0x03, - 0x3c, 0x5e, 0x44, 0xf7, 0xb1, 0xfb, 0x10, 0xd3, 0xb6, 0x16, 0xcc, 0x16, 0xa9, 0x58, 0xb3, 0x66, - 0xb3, 0x69, 0xba, 0xfc, 0x52, 0xef, 0xb1, 0x8d, 0xf5, 0xe2, 0x45, 0xcf, 0xf6, 0xf4, 0x66, 0x9d, - 0xf8, 0x64, 0x75, 0xcf, 0x6c, 0x91, 0xba, 0x69, 0xd5, 0x5b, 0x40, 0x29, 0xb0, 0x4c, 0x66, 0x85, - 0x2b, 0x2c, 0x28, 0x74, 0xad, 0xa1, 0x5b, 0x16, 0x31, 0x2a, 0xd6, 0xf8, 0x9a, 0x47, 0xd8, 0x65, - 0x60, 0x8e, 0x1d, 0x09, 0xb2, 0xb7, 0xe1, 0x0c, 0x4d, 0x19, 0x2f, 0x52, 0x02, 0x2d, 0xa1, 0x90, - 0xfa, 0x1b, 0xf9, 0x84, 0x3c, 0x03, 0x07, 0x48, 0x7d, 0xfc, 0x9e, 0xce, 0x6f, 0xd2, 0xd3, 0x57, - 0x50, 0x1f, 0x0f, 0xf1, 0xc9, 0x2f, 0x18, 0xc0, 0x99, 0xfd, 0x2e, 0x03, 0x89, 0x37, 0x34, 0x9c, - 0x0a, 0x37, 0xd1, 0x99, 0x05, 0xda, 0x4d, 0xc9, 0x9d, 0x59, 0xd8, 0x41, 0x67, 0x76, 0xe1, 0x87, - 0x5f, 0x47, 0xa7, 0x00, 0x9b, 0xd0, 0xad, 0x7d, 0x50, 0x15, 0x44, 0x8e, 0x62, 0x55, 0x25, 0x77, - 0x6e, 0x5a, 0x79, 0xfc, 0x41, 0x34, 0x14, 0x0c, 0x10, 0x93, 0xb8, 0xfc, 0xe6, 0xa2, 0xcb, 0x38, - 0x63, 0x61, 0xd9, 0x28, 0x18, 0x5c, 0xc8, 0xe4, 0xd0, 0x5e, 0x12, 0x2f, 0xf5, 0xdf, 0x64, 0xba, - 0xe5, 0x3b, 0xd8, 0xf7, 0x59, 0xf9, 0x25, 0xd4, 0x67, 0xb0, 0x8f, 0xe2, 0x3a, 0xd5, 0x3d, 0x23, - 0x02, 0x23, 0xd5, 0xfc, 0x32, 0xea, 0x1f, 0x67, 0xba, 0xa6, 0x59, 0x38, 0xe8, 0x9f, 0xf7, 0xd9, - 0x5c, 0xca, 0xe7, 0xf1, 0x49, 0xf4, 0x12, 0x1a, 0x35, 0xc3, 0x88, 0xd5, 0xf5, 0x30, 0xfc, 0x94, - 0x76, 0x4c, 0x80, 0xc3, 0xe8, 0xba, 0x86, 0x4e, 0xfa, 0x8e, 0x8f, 0x8e, 0xef, 0x21, 0xe6, 0xd6, - 0x3b, 0x8e, 0xc9, 0xc6, 0xa5, 0x76, 0xc2, 0x8d, 0xb8, 0x8f, 0xb9, 0x37, 0x1d, 0x93, 0x56, 0xa0, - 0x7b, 0x2b, 0xc4, 0xd2, 0xeb, 0xab, 0xb6, 0x73, 0x07, 0x62, 0x7f, 0xb2, 0xc1, 0xa9, 0x1d, 0x63, - 0xf0, 0xdb, 0x3e, 0x18, 0x3f, 0x84, 0x86, 0x97, 0x9b, 0x1d, 0x12, 0x44, 0x5b, 0x64, 0x77, 0x7d, - 0xda, 0x10, 0x05, 0x06, 0x37, 0x24, 0xe7, 0x10, 0x02, 0x22, 0x0f, 0x92, 0x60, 0xc0, 0xc5, 0x9e, - 0x36, 0x40, 0x21, 0x0b, 0xbc, 0xbb, 0xce, 0x30, 0xad, 0x66, 0x42, 0xaa, 0x37, 0x6d, 0x6b, 0xb9, - 0xee, 0x11, 0xa7, 0x05, 0x0d, 0x05, 0x67, 0x06, 0xed, 0x24, 0x50, 0xc0, 0xd5, 0x89, 0x3b, 0x63, - 0x5b, 0xcb, 0x0b, 0xc4, 0x69, 0xd1, 0xa6, 0x3e, 0x86, 0x30, 0x6f, 0xaa, 0x03, 0x87, 0x1e, 0xec, - 0xe3, 0xc0, 0x9b, 0x41, 0xe3, 0x1f, 0xc1, 0x4e, 0x43, 0xe0, 0xc3, 0x8a, 0x68, 0x90, 0x85, 0x9c, - 0x63, 0x42, 0x03, 0x17, 0x06, 0x0d, 0x31, 0x10, 0xc8, 0xeb, 0x24, 0xe2, 0xde, 0x15, 0xcc, 0xab, - 0x5b, 0xe3, 0xbf, 0xd4, 0x4f, 0xe5, 0x92, 0x32, 0x43, 0xec, 0x4a, 0xd1, 0xc2, 0x69, 0x35, 0xbb, - 0xad, 0x69, 0xf5, 0x98, 0xd5, 0x69, 0xd5, 0xf5, 0x76, 0xbb, 0xbe, 0x64, 0x36, 0xe1, 0x59, 0x15, - 0x2c, 0x7c, 0xda, 0xb0, 0xd5, 0x69, 0x95, 0xda, 0xed, 0x29, 0x06, 0xc4, 0x8f, 0xa2, 0x31, 0x4a, - 0x07, 0x9d, 0x14, 0x50, 0xe6, 0x81, 0x92, 0x32, 0x80, 0x98, 0xad, 0x3e, 0xed, 0x69, 0xd4, 0xcf, - 0x79, 0xb2, 0xb5, 0xaa, 0x57, 0xeb, 0x63, 0xcc, 0x5c, 0xda, 0x73, 0x01, 0x1b, 0x36, 0xb9, 0xf6, - 0x6a, 0x03, 0x7e, 0x79, 0x88, 0x4c, 0x6c, 0x75, 0x5a, 0x2c, 0x22, 0x56, 0x1f, 0x20, 0x83, 0xdf, - 0xf8, 0x02, 0x1a, 0xa1, 0x5c, 0x02, 0x81, 0xb1, 0x60, 0xae, 0xbd, 0x5a, 0x04, 0x8a, 0xaf, 0xa2, - 0x13, 0x12, 0x84, 0xd9, 0xa0, 0xec, 0x99, 0x40, 0xaf, 0x96, 0x88, 0x53, 0xdf, 0xca, 0xc5, 0x13, - 0x5f, 0xec, 0xcb, 0xda, 0x38, 0x8d, 0x10, 0xcf, 0x6b, 0x13, 0x5e, 0xd0, 0x04, 0x5e, 0xcb, 0x21, - 0x26, 0x85, 0x87, 0x50, 0x16, 0x5f, 0x42, 0xfd, 0xec, 0x8b, 0x2a, 0x65, 0xbe, 0x66, 0x82, 0x9b, - 0x91, 0xdb, 0x36, 0x97, 0x96, 0xc0, 0x27, 0x29, 0x40, 0xe3, 0x0b, 0xa8, 0xaf, 0x3c, 0x57, 0xab, - 0x95, 0xe6, 0xfc, 0xdb, 0x46, 0x78, 0xa3, 0x60, 0x58, 0x6e, 0xdd, 0xd5, 0x2d, 0x57, 0xf3, 0x91, - 0xf8, 0x21, 0x54, 0xa8, 0x54, 0x81, 0x8c, 0xbd, 0xbc, 0x1b, 0xdc, 0x58, 0x2f, 0xf6, 0x99, 0x6d, - 0x46, 0xc5, 0x51, 0x50, 0xef, 0xad, 0x4a, 0x59, 0xb8, 0x72, 0x67, 0xf5, 0xde, 0x35, 0x0d, 0xb8, - 0xba, 0xd4, 0x02, 0x34, 0x7e, 0x1a, 0x0d, 0xd5, 0x88, 0x63, 0xea, 0xcd, 0xb9, 0x0e, 0x6c, 0x37, - 0x98, 0x9b, 0xd1, 0xd8, 0xc6, 0x7a, 0x71, 0xd8, 0x05, 0x78, 0xdd, 0x02, 0x84, 0x26, 0x91, 0xe1, - 0xb3, 0x28, 0x3f, 0x6d, 0x5a, 0xbe, 0x1b, 0x3c, 0xf8, 0x49, 0xaf, 0x98, 0x96, 0xa7, 0x01, 0x54, - 0xfd, 0x2f, 0xd9, 0xe4, 0xd4, 0x23, 0xfb, 0x30, 0xb6, 0x76, 0x78, 0x5b, 0x18, 0x51, 0x82, 0xfc, - 0x2e, 0x94, 0x60, 0x09, 0x1d, 0x2b, 0x19, 0x2d, 0xd3, 0x2a, 0xc1, 0x4f, 0x77, 0x76, 0xaa, 0x04, - 0x03, 0x52, 0x78, 0x86, 0x15, 0x41, 0xf3, 0xef, 0x61, 0xe1, 0x56, 0x29, 0xaa, 0xae, 0x33, 0x5c, - 0xbd, 0xb5, 0xa4, 0xd7, 0x1b, 0x2c, 0x6b, 0x87, 0x16, 0x65, 0xaa, 0x7e, 0x5f, 0x76, 0x93, 0x6c, - 0x29, 0x87, 0x51, 0xfa, 0xea, 0xe7, 0xb2, 0xdd, 0x13, 0xd6, 0x1c, 0x4a, 0xa1, 0xfc, 0x79, 0x36, - 0x21, 0x7d, 0xcc, 0xae, 0x24, 0x71, 0x09, 0xf5, 0x33, 0x36, 0x81, 0xbb, 0x26, 0xcc, 0x38, 0x4c, - 0x59, 0x61, 0xa6, 0xf3, 0xd1, 0x78, 0x0e, 0x9d, 0x28, 0x2d, 0x2d, 0x91, 0x86, 0x17, 0x06, 0xde, - 0x9d, 0x0b, 0x83, 0x6d, 0xb2, 0x68, 0xa5, 0x1c, 0x1f, 0x06, 0xee, 0x85, 0xa0, 0x12, 0x89, 0xe5, - 0xf0, 0x02, 0x3a, 0x19, 0x85, 0xd7, 0x98, 0xa9, 0x97, 0x17, 0x02, 0x98, 0xc6, 0x38, 0xb2, 0xff, - 0xb4, 0x94, 0xb2, 0x49, 0xad, 0x84, 0xe9, 0xb4, 0xb7, 0x5b, 0x2b, 0x61, 0x6e, 0x4d, 0x2c, 0xa7, - 0x7e, 0x39, 0x27, 0x66, 0xd9, 0x39, 0xbc, 0x8e, 0x35, 0xd7, 0x24, 0x77, 0xda, 0xad, 0x0e, 0x99, - 0xa7, 0x79, 0xa4, 0x08, 0xa3, 0xe3, 0xf8, 0x9e, 0x67, 0xc1, 0x4b, 0x75, 0x00, 0x8a, 0x3e, 0x64, - 0x01, 0x25, 0xae, 0xa0, 0x7c, 0xc9, 0x59, 0x66, 0x66, 0xcc, 0x66, 0x8f, 0x67, 0x74, 0x67, 0xd9, - 0x4d, 0x7e, 0x3c, 0x43, 0x59, 0xa8, 0xdf, 0x9b, 0xed, 0x92, 0x85, 0xe7, 0x50, 0x4e, 0x22, 0x3f, - 0x92, 0x4d, 0xcb, 0xa7, 0x73, 0x50, 0x5d, 0x84, 0xde, 0x66, 0xe1, 0x1c, 0x6c, 0xff, 0xa9, 0x3d, - 0x16, 0x4e, 0x4a, 0x6e, 0x9f, 0x23, 0xe1, 0x7c, 0x2a, 0x9b, 0x96, 0xde, 0xe8, 0xd0, 0xce, 0x32, - 0x29, 0x19, 0x95, 0x8e, 0x74, 0xe5, 0x47, 0xb3, 0xa9, 0x49, 0xa5, 0x8e, 0xa4, 0xa3, 0x7e, 0x3a, - 0x9b, 0x9a, 0x14, 0xeb, 0x50, 0x0e, 0xa5, 0x44, 0x6d, 0x39, 0x1a, 0x4b, 0x5c, 0x3a, 0xdf, 0x93, - 0x8b, 0xa7, 0x21, 0x3b, 0x94, 0xe7, 0x15, 0xe3, 0xa8, 0xdf, 0xdf, 0xd0, 0x70, 0x1b, 0x7d, 0xcb, - 0xdd, 0xe9, 0x97, 0xc3, 0x2f, 0xa3, 0x63, 0xa1, 0x2c, 0xc5, 0x70, 0x24, 0x70, 0x11, 0xd4, 0xa0, - 0xa8, 0xfa, 0x1b, 0x14, 0xc7, 0xdf, 0xcd, 0x47, 0xa9, 0xd5, 0x6f, 0xe6, 0xe2, 0xb9, 0xdc, 0x8e, - 0x7a, 0x63, 0x87, 0xbd, 0x71, 0x13, 0x9d, 0x9c, 0xe8, 0x38, 0x0e, 0xb1, 0xbc, 0xe4, 0x4e, 0x81, - 0x93, 0xa6, 0x06, 0xa3, 0xa8, 0xc7, 0x3b, 0x27, 0xa5, 0x30, 0x65, 0xcb, 0x5d, 0x2e, 0xa3, 0x6c, - 0xfb, 0x42, 0xb6, 0x1d, 0x46, 0x91, 0xc4, 0x36, 0xb9, 0xb0, 0xfa, 0x3b, 0xd9, 0x78, 0xf6, 0xbd, - 0xa3, 0xae, 0xdf, 0x59, 0xd7, 0x3f, 0x3a, 0xcb, 0xb2, 0x99, 0xdc, 0x30, 0x2d, 0x03, 0x9f, 0x46, - 0xf7, 0xdd, 0xac, 0x4d, 0x6a, 0xf5, 0x1b, 0x95, 0xb9, 0x72, 0xfd, 0xe6, 0x5c, 0xad, 0x3a, 0x39, - 0x51, 0x99, 0xaa, 0x4c, 0x96, 0x47, 0x7b, 0xf0, 0x71, 0x74, 0x2c, 0x44, 0x4d, 0xdf, 0x9c, 0x2d, - 0xcd, 0x8d, 0x66, 0xf0, 0x18, 0x1a, 0x0e, 0x81, 0xe3, 0xf3, 0x0b, 0xa3, 0xd9, 0x47, 0xdf, 0x83, - 0x06, 0xe1, 0xba, 0x86, 0x1d, 0x3b, 0xe2, 0x21, 0xd4, 0x3f, 0x3f, 0x5e, 0x9b, 0xd4, 0x6e, 0x01, - 0x13, 0x84, 0x0a, 0xe5, 0xc9, 0x39, 0xca, 0x30, 0xf3, 0xe8, 0x7f, 0xcf, 0x20, 0x54, 0x9b, 0x5a, - 0xa8, 0x72, 0xc2, 0x41, 0xd4, 0x57, 0x99, 0xbb, 0x55, 0x9a, 0xa9, 0x50, 0xba, 0x7e, 0x94, 0x9f, - 0xaf, 0x4e, 0xd2, 0x1a, 0x06, 0x50, 0xef, 0xc4, 0xcc, 0x7c, 0x6d, 0x72, 0x34, 0x4b, 0x81, 0xda, - 0x64, 0xa9, 0x3c, 0x9a, 0xa3, 0xc0, 0xdb, 0x5a, 0x65, 0x61, 0x72, 0x34, 0x4f, 0xff, 0x9c, 0xa9, - 0x2d, 0x94, 0x16, 0x46, 0x7b, 0xe9, 0x9f, 0x53, 0xf0, 0x67, 0x81, 0x32, 0xab, 0x4d, 0x2e, 0xc0, - 0x8f, 0x3e, 0xda, 0x84, 0x29, 0xff, 0x57, 0x3f, 0x45, 0x51, 0xd6, 0xe5, 0x8a, 0x36, 0x3a, 0x40, - 0x7f, 0x50, 0x96, 0xf4, 0x07, 0xa2, 0x8d, 0xd3, 0x26, 0x67, 0xe7, 0x6f, 0x4d, 0x8e, 0x0e, 0x52, - 0x5e, 0xb3, 0x37, 0x28, 0x78, 0x88, 0xfe, 0xa9, 0xcd, 0xd2, 0x3f, 0x87, 0x29, 0x27, 0x6d, 0xb2, - 0x34, 0x53, 0x2d, 0x2d, 0x4c, 0x8f, 0x8e, 0xd0, 0xf6, 0x00, 0xcf, 0x63, 0xac, 0xe4, 0x5c, 0x69, - 0x76, 0x72, 0x74, 0x94, 0xd3, 0x94, 0x67, 0x2a, 0x73, 0x37, 0x46, 0xc7, 0xa0, 0x21, 0xaf, 0xcf, - 0xc2, 0x0f, 0x4c, 0x0b, 0xc0, 0x5f, 0xc7, 0x1f, 0xfd, 0x30, 0x2a, 0xcc, 0xd7, 0xe0, 0x70, 0xfd, - 0x14, 0x3a, 0x3e, 0x5f, 0xab, 0x2f, 0xbc, 0x5e, 0x9d, 0x8c, 0xc8, 0x7b, 0x0c, 0x0d, 0xfb, 0x88, - 0x99, 0xca, 0xdc, 0xcd, 0x0f, 0x30, 0x69, 0xfb, 0xa0, 0xd9, 0xd2, 0xc4, 0x7c, 0x6d, 0x34, 0x4b, - 0x7b, 0xc5, 0x07, 0xdd, 0xae, 0xcc, 0x95, 0xe7, 0x6f, 0xd7, 0x46, 0x73, 0x8f, 0xde, 0x45, 0x43, - 0x2c, 0x17, 0xcc, 0xbc, 0x63, 0x2e, 0x9b, 0x16, 0x3e, 0x87, 0x4e, 0x97, 0x27, 0x6f, 0x55, 0x26, - 0x26, 0xeb, 0xf3, 0x5a, 0xe5, 0x7a, 0x65, 0x2e, 0x52, 0xd3, 0x7d, 0x68, 0x4c, 0x46, 0x97, 0xaa, - 0x95, 0xd1, 0x0c, 0x3e, 0x89, 0xb0, 0x0c, 0x7e, 0xb5, 0x34, 0x3b, 0x35, 0x9a, 0xc5, 0x0a, 0x3a, - 0x21, 0xc3, 0x2b, 0x73, 0x0b, 0x37, 0xe7, 0x26, 0x47, 0x73, 0x8f, 0xfe, 0x64, 0x06, 0xdd, 0x97, - 0x18, 0x2f, 0x0c, 0xab, 0xe8, 0xfc, 0xe4, 0x4c, 0xa9, 0xb6, 0x50, 0x99, 0xa8, 0x4d, 0x96, 0xb4, - 0x89, 0xe9, 0xfa, 0x44, 0x69, 0x61, 0xf2, 0xfa, 0xbc, 0xf6, 0x7a, 0xfd, 0xfa, 0xe4, 0xdc, 0xa4, - 0x56, 0x9a, 0x19, 0xed, 0xc1, 0x0f, 0xa1, 0x62, 0x0a, 0x4d, 0x6d, 0x72, 0xe2, 0xa6, 0x56, 0x59, - 0x78, 0x7d, 0x34, 0x83, 0x1f, 0x44, 0xe7, 0x52, 0x89, 0xe8, 0xef, 0xd1, 0x2c, 0x3e, 0x8f, 0xce, - 0xa4, 0x91, 0xbc, 0x36, 0x33, 0x9a, 0x7b, 0xf4, 0x87, 0x32, 0x08, 0xc7, 0x03, 0x3e, 0xe1, 0x07, - 0xd0, 0x59, 0xaa, 0x17, 0xf5, 0xf4, 0x06, 0x3e, 0x88, 0xce, 0x25, 0x52, 0x08, 0xcd, 0x2b, 0xa2, - 0xfb, 0x53, 0x48, 0x78, 0xe3, 0xce, 0x22, 0x25, 0x99, 0x00, 0x9a, 0xf6, 0xc5, 0x0c, 0xba, 0x2f, - 0xf1, 0xa4, 0x1f, 0x5f, 0x44, 0x0f, 0x97, 0xca, 0xb3, 0xb4, 0x6f, 0x26, 0x16, 0x2a, 0xf3, 0x73, - 0xb5, 0xfa, 0xec, 0x54, 0xa9, 0x4e, 0xb5, 0xef, 0x66, 0x2d, 0xd2, 0x9b, 0x17, 0x90, 0xda, 0x85, - 0x72, 0x62, 0xba, 0x34, 0x77, 0x9d, 0x0e, 0x3f, 0xfc, 0x30, 0x7a, 0x20, 0x95, 0x6e, 0x72, 0xae, - 0x34, 0x3e, 0x33, 0x59, 0x1e, 0xcd, 0xe2, 0x47, 0xd0, 0x83, 0xa9, 0x54, 0xe5, 0x4a, 0x8d, 0x91, - 0xe5, 0xc6, 0xcb, 0x6f, 0xfd, 0xbb, 0xf3, 0x3d, 0x6f, 0x7d, 0xfd, 0x7c, 0xe6, 0xf7, 0xbe, 0x7e, - 0x3e, 0xf3, 0x27, 0x5f, 0x3f, 0x9f, 0xf9, 0xe0, 0xd5, 0xed, 0x04, 0xf2, 0x62, 0x53, 0xd6, 0x62, - 0x01, 0x4e, 0xdd, 0x9e, 0xfa, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x8a, 0xa1, 0x28, 0x49, 0x4b, - 0x68, 0x01, 0x00, + 0x7c, 0x06, 0xc6, 0xfb, 0x36, 0x37, 0x8a, 0x39, 0xd7, 0x34, 0xb4, 0x10, 0x83, 0x9f, 0x40, 0x7d, + 0xb7, 0x4d, 0x6f, 0x65, 0x76, 0xaa, 0xc4, 0xe5, 0x74, 0x72, 0x73, 0xa3, 0x88, 0xd7, 0x4c, 0x6f, + 0xa5, 0xde, 0x5a, 0xd2, 0x85, 0x0a, 0x7d, 0x32, 0x3c, 0x83, 0x46, 0xab, 0x8e, 0x79, 0x47, 0xf7, + 0xc8, 0x0d, 0xb2, 0x5e, 0xb5, 0x9b, 0x66, 0x63, 0x9d, 0x4b, 0xf1, 0x81, 0xcd, 0x8d, 0xe2, 0xd9, + 0x36, 0xc3, 0xd5, 0x57, 0xc9, 0x7a, 0xbd, 0x0d, 0x58, 0x81, 0x49, 0xac, 0xa4, 0xfa, 0x95, 0x5e, + 0x34, 0x74, 0xd3, 0x25, 0x4e, 0xd0, 0xee, 0x0b, 0x28, 0x4f, 0x7f, 0xf3, 0x26, 0x83, 0xcc, 0x3b, + 0x2e, 0x71, 0x44, 0x99, 0x53, 0x3c, 0xbe, 0x84, 0x7a, 0x67, 0xec, 0x65, 0xd3, 0xe2, 0xcd, 0x3e, + 0xbe, 0xb9, 0x51, 0x3c, 0xd6, 0xa4, 0x00, 0x81, 0x92, 0x51, 0xe0, 0xf7, 0xa3, 0xa1, 0x4a, 0x8b, + 0xea, 0x90, 0x6d, 0xe9, 0x9e, 0xed, 0xf0, 0xd6, 0x82, 0x74, 0x4d, 0x01, 0x2e, 0x14, 0x94, 0xe8, + 0xf1, 0xf3, 0x08, 0x95, 0x6e, 0xd7, 0x34, 0xbb, 0x49, 0x4a, 0xda, 0x1c, 0x57, 0x06, 0x28, 0xad, + 0xaf, 0xb9, 0x75, 0xc7, 0x6e, 0x92, 0xba, 0xee, 0x88, 0xd5, 0x0a, 0xd4, 0x78, 0x12, 0x8d, 0x94, + 0x1a, 0x0d, 0xe2, 0xba, 0x1a, 0xf9, 0x58, 0x87, 0xb8, 0x9e, 0xab, 0xf4, 0x3e, 0x90, 0xbb, 0x38, + 0x30, 0x7e, 0x6e, 0x73, 0xa3, 0x78, 0x5a, 0x07, 0x4c, 0xdd, 0xe1, 0x28, 0x81, 0x45, 0xa4, 0x10, + 0x1e, 0x47, 0xc3, 0xa5, 0x37, 0x3b, 0x0e, 0xa9, 0x18, 0xc4, 0xf2, 0x4c, 0x6f, 0x9d, 0x6b, 0xc8, + 0xd9, 0xcd, 0x8d, 0xa2, 0xa2, 0x53, 0x44, 0xdd, 0xe4, 0x18, 0x81, 0x89, 0x5c, 0x04, 0xcf, 0xa3, + 0xb1, 0xeb, 0x13, 0xd5, 0x1a, 0x71, 0xee, 0x98, 0x0d, 0x52, 0x6a, 0x34, 0xec, 0x8e, 0xe5, 0x29, + 0x7d, 0xc0, 0xe7, 0xc1, 0xcd, 0x8d, 0xe2, 0xb9, 0xe5, 0x46, 0xbb, 0xee, 0x32, 0x6c, 0x5d, 0x67, + 0x68, 0x81, 0x59, 0xbc, 0x2c, 0xfe, 0x20, 0x1a, 0x5e, 0x70, 0xa8, 0x16, 0x1a, 0x65, 0x42, 0xe1, + 0x4a, 0x3f, 0xe8, 0xff, 0xc9, 0xcb, 0x7c, 0x02, 0x62, 0x50, 0xbf, 0x67, 0x59, 0x63, 0x3d, 0x56, + 0xa0, 0x6e, 0x00, 0x4e, 0x6c, 0xac, 0xc4, 0x0a, 0x13, 0xa4, 0xd0, 0x8f, 0x37, 0x1d, 0x62, 0xc4, + 0xb4, 0x6d, 0x00, 0xda, 0x7c, 0x69, 0x73, 0xa3, 0xf8, 0x88, 0xc3, 0x69, 0xea, 0x5d, 0xd5, 0x2e, + 0x95, 0x15, 0x9e, 0x44, 0xfd, 0x54, 0x9b, 0x6e, 0x98, 0x96, 0xa1, 0xa0, 0x07, 0x32, 0x17, 0x47, + 0xae, 0x8e, 0xfa, 0xad, 0xf7, 0xe1, 0xe3, 0xa7, 0x36, 0x37, 0x8a, 0xc7, 0xa9, 0x0e, 0xd6, 0x57, + 0x4d, 0x4b, 0x9c, 0x22, 0x82, 0xa2, 0xea, 0x5f, 0xe4, 0xd1, 0x08, 0x15, 0x8e, 0xa0, 0xc7, 0x25, + 0x3a, 0x24, 0x29, 0x84, 0x8e, 0x50, 0xb7, 0xad, 0x37, 0x08, 0x57, 0x69, 0x60, 0x67, 0xf9, 0x40, + 0x81, 0x5d, 0x94, 0x1e, 0x5f, 0x42, 0xfd, 0x0c, 0x54, 0x29, 0x73, 0x2d, 0x1f, 0xde, 0xdc, 0x28, + 0x0e, 0xb8, 0x00, 0xab, 0x9b, 0x86, 0x16, 0xa0, 0xa9, 0x9a, 0xb1, 0xbf, 0xa7, 0x6d, 0xd7, 0xa3, + 0xcc, 0xb9, 0x92, 0x83, 0x9a, 0xf1, 0x02, 0x2b, 0x1c, 0x25, 0xaa, 0x99, 0x5c, 0x08, 0x3f, 0x87, + 0x10, 0x83, 0x94, 0x0c, 0xc3, 0xe1, 0x9a, 0x7e, 0x7a, 0x73, 0xa3, 0x78, 0x1f, 0x67, 0xa1, 0x1b, + 0x86, 0x38, 0x4c, 0x04, 0x62, 0xdc, 0x42, 0x43, 0xec, 0xd7, 0x8c, 0xbe, 0x48, 0x9a, 0x4c, 0xcd, + 0x07, 0xaf, 0x5e, 0xf4, 0xa5, 0x29, 0x4b, 0xe7, 0xb2, 0x48, 0x3a, 0x69, 0x79, 0xce, 0xfa, 0x78, + 0x91, 0xcf, 0x8c, 0xa7, 0x78, 0x55, 0x4d, 0xc0, 0x89, 0x63, 0x52, 0x2c, 0x43, 0x27, 0xcc, 0x29, + 0xdb, 0x59, 0xd3, 0x1d, 0x83, 0x18, 0xe3, 0xeb, 0xe2, 0x84, 0xb9, 0xe4, 0x83, 0xeb, 0x8b, 0xa2, + 0x0e, 0x88, 0xe4, 0x78, 0x02, 0x0d, 0x33, 0x6e, 0xb5, 0xce, 0x22, 0xf4, 0x7d, 0x5f, 0x4c, 0x5a, + 0x6e, 0x67, 0x31, 0xda, 0xdf, 0x72, 0x19, 0x3a, 0x26, 0x19, 0xe0, 0x16, 0x71, 0xe8, 0x6c, 0x0a, + 0xea, 0xcf, 0xc7, 0x24, 0x67, 0x72, 0x87, 0x61, 0xe2, 0x3c, 0x78, 0x91, 0x33, 0x2f, 0xa3, 0xb1, + 0x98, 0x28, 0xf0, 0x28, 0xca, 0xad, 0x92, 0x75, 0xa6, 0x2e, 0x1a, 0xfd, 0x13, 0x9f, 0x40, 0xbd, + 0x77, 0xf4, 0x66, 0x87, 0xaf, 0x65, 0x1a, 0xfb, 0xf1, 0x7c, 0xf6, 0x7d, 0x19, 0x3a, 0xf5, 0xe3, + 0x09, 0xdb, 0xb2, 0x48, 0xc3, 0x13, 0x67, 0xff, 0x67, 0xd0, 0xc0, 0x8c, 0xdd, 0xd0, 0x9b, 0xd0, + 0x8f, 0x4c, 0xef, 0x94, 0xcd, 0x8d, 0xe2, 0x09, 0xda, 0x81, 0x97, 0x9b, 0x14, 0x23, 0xb4, 0x29, + 0x24, 0xa5, 0x0a, 0xa0, 0x91, 0x96, 0xed, 0x11, 0x28, 0x98, 0x0d, 0x15, 0x00, 0x0a, 0x3a, 0x80, + 0x12, 0x15, 0x20, 0x24, 0xc6, 0x57, 0x50, 0x7f, 0x95, 0x2e, 0x78, 0x0d, 0xbb, 0xc9, 0x95, 0x0f, + 0xe6, 0x64, 0x58, 0x04, 0xc5, 0x41, 0xe3, 0x13, 0xa9, 0xd3, 0x68, 0x64, 0xa2, 0x69, 0x12, 0xcb, + 0x13, 0x5b, 0x4d, 0x87, 0x54, 0x69, 0x99, 0x58, 0x9e, 0xd8, 0x6a, 0x18, 0x7c, 0x3a, 0x85, 0x8a, + 0xad, 0x0e, 0x48, 0xd5, 0xdf, 0xcd, 0xa1, 0xd3, 0x37, 0x3a, 0x8b, 0xc4, 0xb1, 0x88, 0x47, 0x5c, + 0xbe, 0x32, 0x06, 0x5c, 0xe7, 0xd0, 0x58, 0x0c, 0xc9, 0xb9, 0xc3, 0x8a, 0xb5, 0x1a, 0x20, 0xeb, + 0x7c, 0xb1, 0x15, 0xa7, 0xbd, 0x58, 0x51, 0x3c, 0x8d, 0x8e, 0x85, 0x40, 0xda, 0x08, 0x57, 0xc9, + 0xc2, 0x9c, 0x7e, 0x7e, 0x73, 0xa3, 0x78, 0x46, 0xe0, 0x46, 0x9b, 0x2d, 0x6a, 0x70, 0xb4, 0x18, + 0xbe, 0x81, 0x46, 0x43, 0xd0, 0x75, 0xc7, 0xee, 0xb4, 0x5d, 0x25, 0x07, 0xac, 0x8a, 0x9b, 0x1b, + 0xc5, 0xfb, 0x05, 0x56, 0xcb, 0x80, 0x14, 0x57, 0xd2, 0x68, 0x41, 0xfc, 0xed, 0x19, 0x91, 0x1b, + 0x1f, 0x85, 0x79, 0x18, 0x85, 0xcf, 0xfa, 0xa3, 0x30, 0x55, 0x48, 0x97, 0xa3, 0x25, 0xf9, 0xa0, + 0x8c, 0x34, 0x23, 0x36, 0x28, 0x63, 0x35, 0x9e, 0x99, 0x40, 0xf7, 0x25, 0xf2, 0xda, 0x91, 0x56, + 0xff, 0x69, 0x4e, 0xe4, 0x52, 0xb5, 0x8d, 0xa0, 0x33, 0xe7, 0xc5, 0xce, 0xac, 0xda, 0x06, 0x98, + 0x4b, 0x99, 0x70, 0x11, 0x13, 0x1a, 0xdb, 0xb6, 0x8d, 0xa8, 0xd5, 0x14, 0x2f, 0x8b, 0x3f, 0x8a, + 0x4e, 0xc6, 0x80, 0x6c, 0xba, 0x66, 0xda, 0x7f, 0x61, 0x73, 0xa3, 0xa8, 0x26, 0x70, 0x8d, 0xce, + 0xde, 0x29, 0x5c, 0xb0, 0x8e, 0x4e, 0x09, 0x52, 0xb7, 0x2d, 0x4f, 0x37, 0x2d, 0x6e, 0xe5, 0xb1, + 0x51, 0xf2, 0x9e, 0xcd, 0x8d, 0xe2, 0x43, 0xa2, 0x0e, 0xfa, 0x34, 0xd1, 0xc6, 0xa7, 0xf1, 0xc1, + 0x06, 0x52, 0x12, 0x50, 0x95, 0x96, 0xbe, 0xec, 0x9b, 0xae, 0x17, 0x37, 0x37, 0x8a, 0x0f, 0x27, + 0xd6, 0x61, 0x52, 0x2a, 0x71, 0xa9, 0x4c, 0xe3, 0x84, 0x35, 0x84, 0x43, 0xdc, 0x9c, 0x6d, 0x10, + 0xf8, 0x86, 0x5e, 0xe0, 0xaf, 0x6e, 0x6e, 0x14, 0xcf, 0x0b, 0xfc, 0x2d, 0xdb, 0x20, 0xd1, 0xe6, + 0x27, 0x94, 0x56, 0x7f, 0x2d, 0x87, 0xce, 0xd7, 0x4a, 0xb3, 0x33, 0x15, 0xc3, 0xb7, 0x2d, 0xaa, + 0x8e, 0x7d, 0xc7, 0x34, 0x84, 0xd1, 0xbb, 0x88, 0x4e, 0x45, 0x50, 0x93, 0x60, 0xce, 0x04, 0x56, + 0x2d, 0x7c, 0x9b, 0x6f, 0xb7, 0xb4, 0x39, 0x4d, 0x9d, 0xd9, 0x3c, 0x75, 0xc9, 0xa4, 0x4f, 0x63, + 0x44, 0xfb, 0x28, 0x82, 0xaa, 0xad, 0xd8, 0x8e, 0xd7, 0xe8, 0x78, 0x5c, 0x09, 0xa0, 0x8f, 0x62, + 0x75, 0xb8, 0x9c, 0xa8, 0x4b, 0x15, 0x3e, 0x1f, 0xfc, 0xc9, 0x0c, 0x1a, 0x2d, 0x79, 0x9e, 0x63, + 0x2e, 0x76, 0x3c, 0x32, 0xab, 0xb7, 0xdb, 0xa6, 0xb5, 0x0c, 0x63, 0x7d, 0xf0, 0xea, 0x8b, 0xc1, + 0x1a, 0xd9, 0x55, 0x12, 0x97, 0xa3, 0xc5, 0x85, 0x21, 0xaa, 0xfb, 0xa8, 0x7a, 0x8b, 0xe1, 0xc4, + 0x21, 0x1a, 0x2d, 0x47, 0x87, 0x68, 0x22, 0xaf, 0x1d, 0x0d, 0xd1, 0xcf, 0xe5, 0xd0, 0xd9, 0xf9, + 0x55, 0x4f, 0xd7, 0x88, 0x6b, 0x77, 0x9c, 0x06, 0x71, 0x6f, 0xb6, 0x0d, 0xdd, 0x23, 0xe1, 0x48, + 0x2d, 0xa2, 0xde, 0x92, 0x61, 0x10, 0x03, 0xd8, 0xf5, 0xb2, 0xfd, 0x97, 0x4e, 0x01, 0x1a, 0x83, + 0xe3, 0x47, 0x50, 0x1f, 0x2f, 0x03, 0xdc, 0x7b, 0xc7, 0x07, 0x37, 0x37, 0x8a, 0x7d, 0x1d, 0x06, + 0xd2, 0x7c, 0x1c, 0x25, 0x2b, 0x93, 0x26, 0xa1, 0x64, 0xb9, 0x90, 0xcc, 0x60, 0x20, 0xcd, 0xc7, + 0xe1, 0xd7, 0xd0, 0x08, 0xb0, 0x0d, 0xda, 0xc3, 0xe7, 0xbe, 0x13, 0xbe, 0x74, 0xc5, 0xc6, 0xb2, + 0xa5, 0x09, 0x5a, 0x53, 0x77, 0xfc, 0x02, 0x5a, 0x84, 0x01, 0xbe, 0x8d, 0x46, 0x79, 0x23, 0x42, + 0xa6, 0xbd, 0x5d, 0x98, 0xde, 0xb7, 0xb9, 0x51, 0x1c, 0xe3, 0xed, 0x17, 0xd8, 0xc6, 0x98, 0x50, + 0xc6, 0xbc, 0xd9, 0x21, 0xe3, 0xc2, 0x56, 0x8c, 0xf9, 0x17, 0x8b, 0x8c, 0xa3, 0x4c, 0xd4, 0xd7, + 0xd1, 0x90, 0x58, 0x10, 0x9f, 0x84, 0x3d, 0x2e, 0x1b, 0x27, 0xb0, 0x3b, 0x36, 0x0d, 0xd8, 0xd8, + 0x3e, 0x89, 0x06, 0xcb, 0xc4, 0x6d, 0x38, 0x66, 0x9b, 0x5a, 0x0d, 0x5c, 0xc9, 0x8f, 0x6d, 0x6e, + 0x14, 0x07, 0x8d, 0x10, 0xac, 0x89, 0x34, 0xea, 0x7f, 0xcb, 0xa0, 0x93, 0x94, 0x77, 0xc9, 0x75, + 0xcd, 0x65, 0xab, 0x25, 0x2e, 0xdb, 0x8f, 0xa1, 0x42, 0x0d, 0xea, 0xe3, 0x35, 0x9d, 0xd8, 0xdc, + 0x28, 0x8e, 0xb2, 0x16, 0x08, 0x7a, 0xc8, 0x69, 0x82, 0x0d, 0x5e, 0x76, 0x8b, 0x0d, 0x1e, 0x35, + 0x69, 0x3d, 0xdd, 0xf1, 0x4c, 0x6b, 0xb9, 0xe6, 0xe9, 0x5e, 0xc7, 0x95, 0x4c, 0x5a, 0x8e, 0xa9, + 0xbb, 0x80, 0x92, 0x4c, 0x5a, 0xa9, 0x10, 0x7e, 0x19, 0x0d, 0x4d, 0x5a, 0x46, 0xc8, 0x84, 0x4d, + 0x88, 0xf7, 0x53, 0x4b, 0x93, 0x00, 0x3c, 0xce, 0x42, 0x2a, 0xa0, 0xfe, 0x7c, 0x06, 0x29, 0x6c, + 0x37, 0x36, 0x63, 0xba, 0xde, 0x2c, 0x69, 0x2d, 0x0a, 0xb3, 0xd3, 0x94, 0xbf, 0xbd, 0xa3, 0x38, + 0x61, 0x2d, 0x02, 0x53, 0x80, 0x6f, 0xef, 0x9a, 0xa6, 0xeb, 0x45, 0x27, 0xc3, 0x48, 0x29, 0x5c, + 0x41, 0x7d, 0x8c, 0x33, 0xb3, 0x25, 0x06, 0xaf, 0x2a, 0xbe, 0x22, 0x44, 0xab, 0x66, 0xca, 0xd0, + 0x62, 0xc4, 0xe2, 0xfe, 0x9c, 0x97, 0x57, 0x7f, 0x31, 0x8b, 0x46, 0xa3, 0x85, 0xf0, 0x6d, 0xd4, + 0xff, 0xaa, 0x6d, 0x5a, 0xc4, 0x98, 0xb7, 0xa0, 0x85, 0xdd, 0x4f, 0x29, 0x7c, 0x5b, 0xfc, 0xf8, + 0x1b, 0x50, 0xa6, 0x2e, 0x5a, 0xb0, 0x70, 0x68, 0x11, 0x30, 0xc3, 0x1f, 0x44, 0x03, 0xd4, 0x06, + 0xbc, 0x03, 0x9c, 0xb3, 0x5b, 0x72, 0x7e, 0x80, 0x73, 0x3e, 0xe1, 0xb0, 0x42, 0x71, 0xd6, 0x21, + 0x3b, 0xaa, 0x57, 0x1a, 0xd1, 0x5d, 0xdb, 0xe2, 0x3d, 0x0f, 0x7a, 0xe5, 0x00, 0x44, 0xd4, 0x2b, + 0x46, 0x43, 0x4d, 0x57, 0xf6, 0xb1, 0xd0, 0x0d, 0xc2, 0xde, 0x85, 0xc9, 0x2a, 0xda, 0x03, 0x02, + 0xb1, 0xfa, 0x9d, 0x59, 0xf4, 0x78, 0x28, 0x32, 0x8d, 0xdc, 0x31, 0xc9, 0x1a, 0x17, 0xe7, 0x8a, + 0xd9, 0xe6, 0x9b, 0x47, 0xaa, 0xf2, 0xee, 0xc4, 0x8a, 0x6e, 0x2d, 0x13, 0x03, 0x5f, 0x42, 0xbd, + 0x74, 0x87, 0xef, 0x2a, 0x19, 0x30, 0xd7, 0x60, 0x3a, 0x71, 0x28, 0x40, 0x3c, 0x7d, 0x00, 0x0a, + 0x6c, 0xa3, 0xc2, 0x82, 0xa3, 0x9b, 0x9e, 0xdf, 0xb3, 0xa5, 0x78, 0xcf, 0x6e, 0xa3, 0xc6, 0xcb, + 0x8c, 0x07, 0x9b, 0xf3, 0x41, 0x10, 0x1e, 0x00, 0x44, 0x41, 0x30, 0x92, 0x33, 0xcf, 0xa1, 0x41, + 0x81, 0x78, 0x47, 0x93, 0xfa, 0x97, 0xf2, 0xa2, 0xae, 0xfb, 0xcd, 0xe2, 0xba, 0x7e, 0x85, 0xea, + 0xa8, 0xeb, 0x52, 0xab, 0x82, 0x29, 0x39, 0xd7, 0x44, 0x00, 0xc9, 0x9a, 0x08, 0x20, 0xfc, 0x14, + 0xea, 0x67, 0x2c, 0x82, 0xfd, 0x2b, 0xec, 0x7d, 0x1d, 0x80, 0xc9, 0x4b, 0x73, 0x40, 0x88, 0x7f, + 0x36, 0x83, 0xce, 0x75, 0x95, 0x04, 0x28, 0xc3, 0xe0, 0xd5, 0xa7, 0x77, 0x25, 0xc6, 0xf1, 0xc7, + 0x37, 0x37, 0x8a, 0x97, 0x5a, 0x01, 0x49, 0xdd, 0x11, 0x68, 0xea, 0x0d, 0x46, 0x24, 0xb4, 0xab, + 0x7b, 0x53, 0xa8, 0xf1, 0xc8, 0x2a, 0x9d, 0x82, 0x33, 0x1c, 0xab, 0xb1, 0xee, 0x37, 0x32, 0x1f, + 0x1a, 0x8f, 0xfc, 0x7b, 0x97, 0x7c, 0x92, 0x84, 0x6a, 0x52, 0xb8, 0xe0, 0x06, 0x3a, 0xc5, 0x30, + 0x65, 0x7d, 0x7d, 0x7e, 0x69, 0xd6, 0xb6, 0xbc, 0x15, 0xbf, 0x82, 0x5e, 0xf1, 0x10, 0x04, 0x2a, + 0x30, 0xf4, 0xf5, 0xba, 0xbd, 0x54, 0x6f, 0x51, 0xaa, 0x84, 0x3a, 0xd2, 0x38, 0xd1, 0x89, 0x96, + 0x8f, 0x39, 0x7f, 0x0a, 0x2a, 0x84, 0x47, 0x54, 0xfe, 0x38, 0x8d, 0x4f, 0x38, 0x91, 0x42, 0x6a, + 0x05, 0x0d, 0xcd, 0xd8, 0x8d, 0xd5, 0x40, 0x5d, 0x9e, 0x43, 0x85, 0x05, 0xdd, 0x59, 0x26, 0x1e, + 0xc8, 0x62, 0xf0, 0xea, 0xd8, 0x65, 0x76, 0xec, 0x4b, 0x89, 0x18, 0x62, 0x7c, 0x84, 0xcf, 0x06, + 0x05, 0x0f, 0x7e, 0x6b, 0xbc, 0x80, 0xfa, 0xb5, 0x5e, 0x34, 0xc4, 0x8f, 0x28, 0x61, 0x36, 0xc7, + 0xcf, 0x87, 0x87, 0xbe, 0x7c, 0xfa, 0x0a, 0x8e, 0x69, 0x82, 0xe3, 0xa5, 0x21, 0xca, 0xec, 0xf7, + 0x36, 0x8a, 0x99, 0xcd, 0x8d, 0x62, 0x8f, 0xd6, 0x2f, 0x6c, 0x2a, 0xc3, 0xf5, 0x46, 0x58, 0x60, + 0xc5, 0x43, 0xc7, 0x48, 0x59, 0xb6, 0xfe, 0xbc, 0x8c, 0xfa, 0x78, 0x1b, 0xb8, 0xc6, 0x9d, 0x0a, + 0xcf, 0x32, 0xa4, 0xa3, 0xd6, 0x48, 0x69, 0xbf, 0x14, 0x7e, 0x11, 0x15, 0xd8, 0xde, 0x9e, 0x0b, + 0xe0, 0x64, 0xf2, 0x59, 0x48, 0xa4, 0x38, 0x2f, 0x83, 0xa7, 0x11, 0x0a, 0xf7, 0xf5, 0xc1, 0xc9, + 0x32, 0xe7, 0x10, 0xdf, 0xf1, 0x47, 0xb8, 0x08, 0x65, 0xf1, 0x33, 0x68, 0x68, 0x81, 0x38, 0x2d, + 0xd3, 0xd2, 0x9b, 0x35, 0xf3, 0x4d, 0xff, 0x70, 0x19, 0x16, 0x5e, 0xd7, 0x7c, 0x53, 0x1c, 0xb9, + 0x12, 0x1d, 0xfe, 0x48, 0xd2, 0xbe, 0xb9, 0x0f, 0x1a, 0xf2, 0xe0, 0x96, 0x1b, 0xca, 0x48, 0x7b, + 0x12, 0xb6, 0xd1, 0xaf, 0xa1, 0x61, 0x69, 0xcb, 0xc4, 0x4f, 0x0f, 0xcf, 0xc5, 0x59, 0x0b, 0xfb, + 0xbf, 0x08, 0x5b, 0x99, 0x03, 0xd5, 0xe4, 0x8a, 0x65, 0x7a, 0xa6, 0xde, 0x9c, 0xb0, 0x5b, 0x2d, + 0xdd, 0x32, 0x94, 0x81, 0x50, 0x93, 0x4d, 0x86, 0xa9, 0x37, 0x18, 0x4a, 0xd4, 0x64, 0xb9, 0x10, + 0xdd, 0x96, 0xf3, 0x3e, 0xd4, 0x48, 0xc3, 0x76, 0xa8, 0x2d, 0x00, 0x87, 0x83, 0x7c, 0x5b, 0xee, + 0x32, 0x5c, 0xdd, 0xf1, 0x91, 0xa2, 0xb1, 0x1d, 0x2d, 0xf8, 0x6a, 0xbe, 0x7f, 0x70, 0x74, 0x28, + 0x7a, 0x9e, 0xab, 0xfe, 0x4c, 0x0e, 0x0d, 0x72, 0x52, 0xba, 0x94, 0x1e, 0x29, 0xf8, 0x5e, 0x14, + 0x3c, 0x51, 0x51, 0x0b, 0xfb, 0xa5, 0xa8, 0xea, 0xa7, 0xb2, 0xc1, 0x6c, 0x54, 0x75, 0x4c, 0x6b, + 0x6f, 0xb3, 0xd1, 0x05, 0x84, 0x26, 0x56, 0x3a, 0xd6, 0x2a, 0xbb, 0xb7, 0xca, 0x86, 0xf7, 0x56, + 0x0d, 0x53, 0x13, 0x30, 0xf8, 0x1c, 0xca, 0x97, 0x29, 0x7f, 0xda, 0x33, 0x43, 0xe3, 0x03, 0x6f, + 0x31, 0x4e, 0x99, 0xc7, 0x35, 0x00, 0xd3, 0xcd, 0xd5, 0xf8, 0xba, 0x47, 0x98, 0x39, 0x9b, 0x63, + 0x9b, 0xab, 0x45, 0x0a, 0xd0, 0x18, 0x1c, 0x5f, 0x43, 0x63, 0x65, 0xd2, 0xd4, 0xd7, 0x67, 0xcd, + 0x66, 0xd3, 0x74, 0x49, 0xc3, 0xb6, 0x0c, 0x17, 0x84, 0xcc, 0xab, 0x6b, 0xb9, 0x5a, 0x9c, 0x00, + 0xab, 0xa8, 0x30, 0xbf, 0xb4, 0xe4, 0x12, 0x0f, 0xc4, 0x97, 0x1b, 0x47, 0x74, 0x72, 0xb6, 0x01, + 0xa2, 0x71, 0x8c, 0xfa, 0x85, 0x0c, 0xdd, 0xbd, 0xb8, 0xab, 0x9e, 0xdd, 0x0e, 0xb4, 0x7c, 0x4f, + 0x22, 0xb9, 0x14, 0xda, 0x15, 0x59, 0xf8, 0xda, 0x63, 0xfc, 0x6b, 0xfb, 0xb8, 0x6d, 0x11, 0x5a, + 0x14, 0x89, 0x5f, 0x95, 0xdb, 0xe2, 0xab, 0xd4, 0x3f, 0xcf, 0xa2, 0x53, 0xbc, 0xc5, 0x13, 0x4d, + 0xb3, 0xbd, 0x68, 0xeb, 0x8e, 0xa1, 0x91, 0x06, 0x31, 0xef, 0x90, 0xc3, 0x39, 0xf0, 0xe4, 0xa1, + 0x93, 0xdf, 0xc3, 0xd0, 0xb9, 0x0a, 0x1b, 0x41, 0x2a, 0x19, 0x38, 0xf0, 0x65, 0x46, 0xc5, 0xe8, + 0xe6, 0x46, 0x71, 0xc8, 0x60, 0x60, 0x38, 0xf2, 0xd7, 0x44, 0x22, 0xaa, 0x24, 0x33, 0xc4, 0x5a, + 0xf6, 0x56, 0x40, 0x49, 0x7a, 0x99, 0x92, 0x34, 0x01, 0xa2, 0x71, 0x8c, 0xfa, 0x67, 0x59, 0x74, + 0x22, 0x2a, 0xf2, 0x1a, 0xb1, 0x8c, 0x23, 0x79, 0xbf, 0x3d, 0xf2, 0xfe, 0x46, 0x0e, 0xdd, 0xcf, + 0xcb, 0xd4, 0x56, 0x74, 0x87, 0x18, 0x65, 0xd3, 0x21, 0x0d, 0xcf, 0x76, 0xd6, 0x0f, 0xb1, 0x01, + 0xb5, 0x7f, 0x62, 0xbf, 0x86, 0x0a, 0x7c, 0xfb, 0xcf, 0xd6, 0x99, 0x91, 0xa0, 0x25, 0x00, 0x8d, + 0xad, 0x50, 0xec, 0xe8, 0x20, 0xd2, 0x59, 0x85, 0xed, 0x74, 0xd6, 0xfb, 0xd0, 0x70, 0x20, 0x7a, + 0xd8, 0x88, 0xf6, 0x85, 0xd6, 0x96, 0xe1, 0x23, 0x60, 0x2f, 0xaa, 0xc9, 0x84, 0x50, 0x9b, 0x0f, + 0xa8, 0x94, 0xc1, 0x1a, 0x1a, 0xe6, 0xb5, 0x05, 0xe5, 0x4c, 0x43, 0x13, 0x89, 0xd4, 0x8d, 0x3c, + 0x3a, 0x93, 0xdc, 0xed, 0x1a, 0xd1, 0x8d, 0xa3, 0x5e, 0x7f, 0x57, 0xf6, 0x3a, 0x7e, 0x10, 0xe5, + 0xab, 0xba, 0xb7, 0xc2, 0xef, 0xc1, 0xe1, 0x4e, 0x78, 0xc9, 0x6c, 0x92, 0x7a, 0x5b, 0xf7, 0x56, + 0x34, 0x40, 0x09, 0x73, 0x06, 0x02, 0x8e, 0x09, 0x73, 0x86, 0xb0, 0xd8, 0x0f, 0x3e, 0x90, 0xb9, + 0x98, 0x4f, 0x5c, 0xec, 0xbf, 0x96, 0x4f, 0x9b, 0x57, 0x6e, 0x3b, 0xa6, 0x47, 0x8e, 0x34, 0xec, + 0x48, 0xc3, 0xf6, 0xa8, 0x61, 0x7f, 0x90, 0x45, 0xc3, 0xc1, 0xa6, 0xe9, 0x0d, 0xd2, 0x38, 0x98, + 0xb5, 0x2a, 0xdc, 0xca, 0xe4, 0xf6, 0xbc, 0x95, 0xd9, 0x8b, 0x42, 0xa9, 0xc1, 0x91, 0x27, 0x33, + 0x0d, 0x40, 0x62, 0xec, 0xc8, 0x33, 0x38, 0xe8, 0x7c, 0x10, 0xf5, 0xcd, 0xea, 0x77, 0xcd, 0x56, + 0xa7, 0xc5, 0xad, 0x74, 0xf0, 0xeb, 0x6a, 0xe9, 0x77, 0x35, 0x1f, 0xae, 0xfe, 0xcb, 0x0c, 0x1a, + 0xe1, 0x42, 0xe5, 0xcc, 0xf7, 0x24, 0xd5, 0x50, 0x3a, 0xd9, 0x3d, 0x4b, 0x27, 0xb7, 0x7b, 0xe9, + 0xa8, 0x3f, 0x96, 0x43, 0xca, 0x94, 0xd9, 0x24, 0x0b, 0x8e, 0x6e, 0xb9, 0x4b, 0xc4, 0xe1, 0xdb, + 0xe9, 0x49, 0xca, 0x6a, 0x4f, 0x1f, 0x28, 0x4c, 0x29, 0xd9, 0x5d, 0x4d, 0x29, 0xef, 0x45, 0x03, + 0xbc, 0x31, 0x81, 0x4f, 0x21, 0x8c, 0x1a, 0xc7, 0x07, 0x6a, 0x21, 0x9e, 0x12, 0x97, 0xda, 0x6d, + 0xc7, 0xbe, 0x43, 0x1c, 0x76, 0x4b, 0xc5, 0x89, 0x75, 0x1f, 0xa8, 0x85, 0x78, 0x81, 0x33, 0xf1, + 0xed, 0x45, 0x91, 0x33, 0x71, 0xb4, 0x10, 0x8f, 0x2f, 0xa2, 0xfe, 0x19, 0xbb, 0xa1, 0x83, 0xa0, + 0xd9, 0xb4, 0x32, 0xb4, 0xb9, 0x51, 0xec, 0x6f, 0x72, 0x98, 0x16, 0x60, 0x29, 0x65, 0xd9, 0x5e, + 0xb3, 0x9a, 0xb6, 0xce, 0x9c, 0x5f, 0xfa, 0x19, 0xa5, 0xc1, 0x61, 0x5a, 0x80, 0xa5, 0x94, 0x54, + 0xe6, 0xe0, 0x54, 0xd4, 0x1f, 0xf2, 0x5c, 0xe2, 0x30, 0x2d, 0xc0, 0xaa, 0x5f, 0xc8, 0x53, 0xed, + 0x75, 0xcd, 0x37, 0xef, 0xf9, 0x75, 0x21, 0x1c, 0x30, 0xbd, 0xbb, 0x18, 0x30, 0xf7, 0xcc, 0x81, + 0x9d, 0xfa, 0x17, 0x7d, 0x08, 0x71, 0xe9, 0x4f, 0x1e, 0x6d, 0x0e, 0xf7, 0xa6, 0x35, 0x65, 0x34, + 0x36, 0x69, 0xad, 0xe8, 0x56, 0x83, 0x18, 0xe1, 0xb1, 0x65, 0x01, 0x86, 0x36, 0xf8, 0xf4, 0x12, + 0x8e, 0x0c, 0xcf, 0x2d, 0xb5, 0x78, 0x01, 0xfc, 0x24, 0x1a, 0xac, 0x58, 0x1e, 0x71, 0xf4, 0x86, + 0x67, 0xde, 0x21, 0x7c, 0x6a, 0x80, 0x9b, 0x61, 0x33, 0x04, 0x6b, 0x22, 0x0d, 0xbe, 0x86, 0x86, + 0xaa, 0xba, 0xe3, 0x99, 0x0d, 0xb3, 0xad, 0x5b, 0x9e, 0xab, 0xf4, 0xc3, 0x8c, 0x06, 0x16, 0x46, + 0x5b, 0x80, 0x6b, 0x12, 0x15, 0xfe, 0x08, 0x1a, 0x80, 0xad, 0x29, 0x38, 0x4e, 0x0f, 0x6c, 0x79, + 0x71, 0xf8, 0x50, 0xe8, 0x1e, 0xc8, 0x4e, 0x5f, 0xe1, 0x06, 0x38, 0x7a, 0x77, 0x18, 0x70, 0xc4, + 0x1f, 0x40, 0x7d, 0x93, 0x96, 0x01, 0xcc, 0xd1, 0x96, 0xcc, 0x55, 0xce, 0xfc, 0x64, 0xc8, 0xdc, + 0x6e, 0x47, 0x78, 0xfb, 0xec, 0x92, 0x47, 0xd9, 0xe0, 0xdb, 0x37, 0xca, 0x86, 0xde, 0x86, 0x63, + 0xf1, 0xe1, 0xfd, 0x3a, 0x16, 0x1f, 0xd9, 0xe5, 0xb1, 0xb8, 0xfa, 0x26, 0x1a, 0x1c, 0xaf, 0x4e, + 0x05, 0xa3, 0xf7, 0x34, 0xca, 0x55, 0xb9, 0xa7, 0x42, 0x9e, 0xd9, 0x33, 0x6d, 0xd3, 0xd0, 0x28, + 0x0c, 0x5f, 0x42, 0xfd, 0x13, 0xe0, 0xfe, 0xc6, 0x6f, 0x11, 0xf3, 0x6c, 0xfd, 0x6b, 0x00, 0x0c, + 0xbc, 0x60, 0x7d, 0x34, 0x7e, 0x04, 0xf5, 0x55, 0x1d, 0x7b, 0xd9, 0xd1, 0x5b, 0x7c, 0x0d, 0x06, + 0x57, 0x91, 0x36, 0x03, 0x69, 0x3e, 0x4e, 0xfd, 0xbe, 0x8c, 0x6f, 0xb6, 0xd3, 0x12, 0xb5, 0x0e, + 0x1c, 0xcd, 0x43, 0xdd, 0xfd, 0xac, 0x84, 0xcb, 0x40, 0x9a, 0x8f, 0xc3, 0x97, 0x50, 0xef, 0xa4, + 0xe3, 0xd8, 0x8e, 0xe8, 0x6c, 0x4e, 0x28, 0x40, 0xbc, 0xee, 0x05, 0x0a, 0xfc, 0x2c, 0x1a, 0x64, + 0x73, 0x0e, 0x3b, 0xd1, 0xcc, 0x75, 0xbb, 0x29, 0x15, 0x29, 0xd5, 0xaf, 0xe4, 0x04, 0x9b, 0x8d, + 0x49, 0xfc, 0x1e, 0xbc, 0x15, 0x78, 0x0a, 0xe5, 0xc6, 0xab, 0x53, 0x7c, 0x02, 0x3c, 0xee, 0x17, + 0x15, 0x54, 0x25, 0x52, 0x8e, 0x52, 0xe3, 0xb3, 0x28, 0x5f, 0xa5, 0xea, 0x53, 0x00, 0xf5, 0xe8, + 0xdf, 0xdc, 0x28, 0xe6, 0xdb, 0x54, 0x7f, 0x00, 0x0a, 0x58, 0xba, 0x99, 0x61, 0x3b, 0x26, 0x86, + 0x0d, 0xf7, 0x31, 0x67, 0x51, 0xbe, 0xe4, 0x2c, 0xdf, 0xe1, 0xb3, 0x16, 0x60, 0x75, 0x67, 0xf9, + 0x8e, 0x06, 0x50, 0x7c, 0x05, 0x21, 0x8d, 0x78, 0x1d, 0xc7, 0x82, 0x77, 0x20, 0x03, 0x70, 0xfe, + 0x06, 0xb3, 0xa1, 0x03, 0xd0, 0x7a, 0xc3, 0x36, 0x88, 0x26, 0x90, 0xa8, 0x3f, 0x15, 0x5e, 0xec, + 0x94, 0x4d, 0x77, 0xf5, 0xa8, 0x0b, 0x77, 0xd0, 0x85, 0x3a, 0x3f, 0xe2, 0x8c, 0x77, 0x52, 0x11, + 0xf5, 0x4e, 0x35, 0xf5, 0x65, 0x17, 0xfa, 0x90, 0xfb, 0x92, 0x2d, 0x51, 0x80, 0xc6, 0xe0, 0x91, + 0x7e, 0xea, 0xdf, 0xba, 0x9f, 0x7e, 0xb0, 0x37, 0x18, 0x6d, 0x73, 0xc4, 0x5b, 0xb3, 0x9d, 0xa3, + 0xae, 0xda, 0x6e, 0x57, 0x5d, 0x40, 0x7d, 0x35, 0xa7, 0x21, 0x1c, 0x5d, 0xc0, 0x7e, 0xc0, 0x75, + 0x1a, 0xec, 0xd8, 0xc2, 0x47, 0x52, 0xba, 0xb2, 0xeb, 0x01, 0x5d, 0x5f, 0x48, 0x67, 0xb8, 0x1e, + 0xa7, 0xe3, 0x48, 0x4e, 0x57, 0xb5, 0x1d, 0x8f, 0x77, 0x5c, 0x40, 0xd7, 0xb6, 0x1d, 0x4f, 0xf3, + 0x91, 0xf8, 0xbd, 0x08, 0x2d, 0x4c, 0x54, 0x7d, 0x67, 0xfb, 0x81, 0xd0, 0x17, 0x90, 0x7b, 0xd9, + 0x6b, 0x02, 0x1a, 0x2f, 0xa0, 0x81, 0xf9, 0x36, 0x71, 0xd8, 0x56, 0x88, 0xbd, 0xec, 0x78, 0x4f, + 0x44, 0xb4, 0xbc, 0xdf, 0x2f, 0xf3, 0xff, 0x03, 0x72, 0xb6, 0xbe, 0xd8, 0xfe, 0x4f, 0x2d, 0x64, + 0x84, 0x9f, 0x45, 0x85, 0x12, 0xb3, 0xf3, 0x06, 0x81, 0x65, 0x20, 0x32, 0xd8, 0x82, 0x32, 0x14, + 0xdb, 0xb3, 0xeb, 0xf0, 0xb7, 0xc6, 0xc9, 0xd5, 0x4b, 0x68, 0x34, 0x5a, 0x0d, 0x1e, 0x44, 0x7d, + 0x13, 0xf3, 0x73, 0x73, 0x93, 0x13, 0x0b, 0xa3, 0x3d, 0xb8, 0x1f, 0xe5, 0x6b, 0x93, 0x73, 0xe5, + 0xd1, 0x8c, 0xfa, 0x73, 0xc2, 0x0c, 0x42, 0x55, 0xeb, 0xe8, 0x6a, 0x78, 0x4f, 0xf7, 0x2d, 0xa3, + 0x70, 0x1f, 0x0a, 0x27, 0x06, 0x2d, 0xd3, 0xf3, 0x88, 0xc1, 0x57, 0x09, 0xb8, 0x2f, 0xf4, 0xee, + 0x6a, 0x31, 0x3c, 0x7e, 0x0c, 0x0d, 0x03, 0x8c, 0x5f, 0x11, 0xb2, 0xfd, 0x31, 0x2f, 0xe0, 0xdc, + 0xd5, 0x64, 0xa4, 0xfa, 0xd5, 0xf0, 0x76, 0x78, 0x86, 0xe8, 0x87, 0xf5, 0x46, 0xf1, 0x1d, 0xd2, + 0x5f, 0xea, 0x5f, 0xe5, 0xd9, 0x13, 0x10, 0xf6, 0x70, 0xef, 0x20, 0x44, 0x19, 0x1e, 0xe9, 0xe6, + 0x76, 0x70, 0xa4, 0xfb, 0x18, 0x2a, 0xcc, 0x12, 0x6f, 0xc5, 0xf6, 0x1d, 0xbf, 0xc0, 0x43, 0xaf, + 0x05, 0x10, 0xd1, 0x43, 0x8f, 0xd1, 0xe0, 0x55, 0x84, 0xfd, 0x57, 0x79, 0x81, 0x23, 0xb6, 0x7f, + 0x84, 0x7c, 0x2a, 0xb6, 0x4f, 0xa9, 0xc1, 0x93, 0x5c, 0xf0, 0xb1, 0x3f, 0x11, 0x38, 0x7a, 0x0b, + 0x9e, 0x58, 0x7f, 0xb9, 0x51, 0x2c, 0x30, 0x1a, 0x2d, 0x81, 0x2d, 0x7e, 0x0d, 0x0d, 0xcc, 0x4e, + 0x95, 0xf8, 0x0b, 0x3d, 0xe6, 0x15, 0x71, 0x3a, 0x90, 0xa2, 0x8f, 0x08, 0x44, 0x02, 0xef, 0x6d, + 0x5a, 0x4b, 0x7a, 0xfc, 0x81, 0x5e, 0xc8, 0x85, 0x6a, 0x0b, 0x7b, 0xb9, 0xc3, 0x4f, 0x17, 0x02, + 0x6d, 0x91, 0xdf, 0xf3, 0x44, 0x65, 0xc5, 0xb0, 0x11, 0x6d, 0xe9, 0xdf, 0xc3, 0xe8, 0x9e, 0x47, + 0x63, 0xa5, 0x76, 0xbb, 0x69, 0x12, 0x03, 0xf4, 0x45, 0xeb, 0x34, 0x89, 0xcb, 0x5d, 0x7e, 0xe0, + 0x31, 0x88, 0xce, 0x90, 0x75, 0x78, 0x17, 0x5a, 0x77, 0x3a, 0xb2, 0x7f, 0x66, 0xbc, 0xac, 0xfa, + 0x03, 0x59, 0x74, 0x72, 0xc2, 0x21, 0xba, 0x47, 0x66, 0xa7, 0x4a, 0xa5, 0x0e, 0xf8, 0xc8, 0x35, + 0x9b, 0xc4, 0x5a, 0x3e, 0x98, 0x61, 0xfd, 0x02, 0x1a, 0x09, 0x1a, 0x50, 0x6b, 0xd8, 0x6d, 0x22, + 0x3e, 0xac, 0x6a, 0xf8, 0x98, 0xba, 0x4b, 0x51, 0x5a, 0x84, 0x14, 0xdf, 0x40, 0xc7, 0x03, 0x48, + 0xa9, 0xd9, 0xb4, 0xd7, 0x34, 0xd2, 0x71, 0x99, 0x63, 0x6c, 0x3f, 0x73, 0x8c, 0x0d, 0x39, 0xe8, + 0x14, 0x5f, 0x77, 0x28, 0x81, 0x96, 0x54, 0x4a, 0xfd, 0x7c, 0x0e, 0x9d, 0xba, 0xa5, 0x37, 0x4d, + 0x23, 0x14, 0x8d, 0x46, 0xdc, 0xb6, 0x6d, 0xb9, 0xe4, 0x10, 0x8d, 0x52, 0x69, 0x28, 0xe4, 0xf7, + 0x65, 0x28, 0xc4, 0xbb, 0xa8, 0x77, 0xcf, 0x5d, 0x54, 0xd8, 0x55, 0x17, 0xfd, 0xa7, 0x0c, 0x1a, + 0xf5, 0x1d, 0xff, 0xc5, 0xd7, 0xd4, 0x82, 0x57, 0x3a, 0x1c, 0x21, 0x46, 0xfc, 0xa0, 0x01, 0x8f, + 0x6b, 0xa8, 0x6f, 0xf2, 0x6e, 0xdb, 0x74, 0x88, 0xbb, 0x0d, 0x27, 0xee, 0x73, 0xfc, 0xb8, 0x64, + 0x8c, 0xb0, 0x22, 0xb1, 0x93, 0x12, 0x06, 0x86, 0xe7, 0x7c, 0xec, 0xe9, 0xc3, 0xb8, 0xff, 0x44, + 0x9c, 0x3d, 0xe7, 0xe3, 0x4f, 0x24, 0xa4, 0xf7, 0x99, 0x21, 0x29, 0x7e, 0x08, 0xe5, 0x16, 0x16, + 0x66, 0xf8, 0x4c, 0x0a, 0x4f, 0xf3, 0x3d, 0x4f, 0x7c, 0xaf, 0x48, 0xb1, 0xea, 0x1f, 0x67, 0x11, + 0xa2, 0xaa, 0xc0, 0x86, 0xeb, 0x81, 0x28, 0xe1, 0x38, 0xea, 0xf7, 0x05, 0xce, 0xd5, 0x30, 0xf0, + 0xda, 0x8f, 0x76, 0x44, 0xb4, 0xee, 0xe0, 0x85, 0x46, 0xd1, 0x77, 0x24, 0x67, 0xf7, 0x00, 0xb0, + 0xb3, 0x01, 0x47, 0x72, 0xdf, 0x7d, 0xfc, 0xbd, 0x68, 0x80, 0xcf, 0x78, 0xb6, 0x74, 0xfe, 0xdf, + 0xf0, 0x81, 0x5a, 0x88, 0x8f, 0x4c, 0xad, 0x85, 0x3d, 0x2c, 0xc4, 0xbe, 0x78, 0x59, 0xaf, 0x1c, + 0x89, 0x77, 0x9f, 0xc5, 0xfb, 0x19, 0x2e, 0x5e, 0xf6, 0x82, 0xe7, 0xd0, 0x8a, 0x77, 0xdf, 0xce, + 0xbe, 0xd5, 0x3f, 0xc8, 0x20, 0x4c, 0x9b, 0x55, 0xd5, 0x5d, 0x77, 0xcd, 0x76, 0x0c, 0xe6, 0x9c, + 0x7e, 0x20, 0x82, 0xd9, 0xbf, 0xfb, 0xca, 0xaf, 0xf4, 0xa3, 0xe3, 0x92, 0xe3, 0xef, 0x21, 0x9f, + 0xac, 0x2e, 0xc9, 0xa3, 0xa9, 0xdb, 0xab, 0x97, 0x87, 0xc5, 0x0b, 0xd1, 0x5e, 0xe9, 0x01, 0x9a, + 0x70, 0x13, 0xfa, 0x38, 0x1a, 0xe2, 0x3f, 0xe8, 0x0a, 0xed, 0xdf, 0x74, 0xc1, 0x28, 0x75, 0x29, + 0x40, 0x93, 0xd0, 0xf8, 0x69, 0x34, 0x40, 0x07, 0xcc, 0x32, 0x44, 0xf1, 0xe8, 0x0b, 0x5f, 0x94, + 0x18, 0x3e, 0x50, 0x5c, 0x4f, 0x02, 0x4a, 0xe1, 0x1d, 0x51, 0xff, 0x36, 0xde, 0x11, 0x7d, 0x14, + 0x0d, 0x96, 0x2c, 0xcb, 0xf6, 0x60, 0x93, 0xee, 0xf2, 0xab, 0x89, 0x54, 0xab, 0xfc, 0x21, 0x78, + 0x1c, 0x1f, 0xd2, 0x27, 0x9a, 0xe5, 0x22, 0x43, 0x7c, 0xd5, 0x7f, 0x15, 0x43, 0x1c, 0xee, 0x55, + 0x0e, 0xd7, 0x33, 0x0e, 0x87, 0xc5, 0x1f, 0xc5, 0x40, 0xe7, 0x0d, 0x57, 0x1d, 0xbb, 0x6d, 0xbb, + 0xc4, 0x60, 0x82, 0x1a, 0x0c, 0x43, 0x0d, 0xb4, 0x39, 0x02, 0xde, 0xb1, 0x49, 0x11, 0x35, 0xa4, + 0x22, 0x78, 0x09, 0x9d, 0xf0, 0x2f, 0x8a, 0x83, 0x17, 0x83, 0x95, 0xb2, 0xab, 0x0c, 0xc1, 0xab, + 0x24, 0x1c, 0x55, 0x86, 0x4a, 0x79, 0xfc, 0xbc, 0x7f, 0x2d, 0xe2, 0x3f, 0x39, 0xac, 0x9b, 0x86, + 0xd8, 0xd5, 0x89, 0xfc, 0xf0, 0xb7, 0xa0, 0xc1, 0x59, 0xfd, 0x6e, 0xb9, 0xc3, 0xcf, 0x5e, 0x86, + 0xb7, 0x7f, 0xfb, 0xd2, 0xd2, 0xef, 0xd6, 0x0d, 0x5e, 0x2e, 0x62, 0x53, 0x88, 0x2c, 0x71, 0x1d, + 0x9d, 0xac, 0x3a, 0x76, 0xcb, 0xf6, 0x88, 0x11, 0x79, 0x7c, 0x77, 0x2c, 0x7c, 0xad, 0xdb, 0xe6, + 0x14, 0xf5, 0x2e, 0xaf, 0xf0, 0x52, 0xd8, 0xe0, 0x16, 0x3a, 0x56, 0x72, 0xdd, 0x4e, 0x8b, 0x84, + 0x37, 0x54, 0xa3, 0x5b, 0x7e, 0xc6, 0x7b, 0xb8, 0xd7, 0xf2, 0xfd, 0x3a, 0x14, 0x65, 0x17, 0x54, + 0x75, 0xcf, 0x14, 0x6b, 0x84, 0x6f, 0x89, 0xf2, 0x7e, 0x35, 0xdf, 0x3f, 0x32, 0x7a, 0x4c, 0x3b, + 0x15, 0x6f, 0xcc, 0x82, 0xe9, 0x35, 0x89, 0xfa, 0xe5, 0x0c, 0x42, 0xa1, 0x80, 0xf1, 0xe3, 0x72, + 0xa8, 0xa0, 0x4c, 0x78, 0xd1, 0xc1, 0xa3, 0x17, 0x48, 0xb1, 0x81, 0xf0, 0x59, 0x94, 0x87, 0x08, + 0x17, 0xd9, 0xf0, 0x60, 0x75, 0xd5, 0xb4, 0x0c, 0x0d, 0xa0, 0x14, 0x2b, 0x3c, 0x45, 0x07, 0x2c, + 0x5c, 0xea, 0x33, 0xab, 0xb0, 0x8c, 0x8e, 0xd5, 0x3a, 0x8b, 0x7e, 0xdd, 0xc2, 0xbb, 0x3a, 0x08, + 0xb4, 0xe1, 0x76, 0x16, 0x83, 0xc7, 0xa8, 0x52, 0x18, 0x13, 0xb9, 0x88, 0xfa, 0x85, 0x4c, 0x64, + 0x16, 0x3c, 0xc0, 0x45, 0xef, 0xe1, 0xb8, 0x9f, 0x46, 0x7c, 0x5a, 0x52, 0xff, 0x56, 0x16, 0x0d, + 0x56, 0x6d, 0xc7, 0xe3, 0x21, 0x43, 0x0e, 0xf7, 0x2a, 0x24, 0xec, 0x95, 0xf2, 0x3b, 0xd8, 0x2b, + 0x9d, 0x45, 0x79, 0xc1, 0x45, 0x99, 0xdd, 0x8b, 0x18, 0x86, 0xa3, 0x01, 0x54, 0xfd, 0xd6, 0x2c, + 0x42, 0x1f, 0x78, 0xf2, 0xc9, 0x7b, 0x58, 0x40, 0xea, 0x8f, 0x66, 0xd0, 0x31, 0x7e, 0x51, 0x27, + 0x04, 0xdd, 0xea, 0xf3, 0xaf, 0x58, 0xc5, 0x71, 0xc9, 0x40, 0x9a, 0x8f, 0xa3, 0x4b, 0xc0, 0xe4, + 0x5d, 0xd3, 0x83, 0xbb, 0x0a, 0x21, 0xea, 0x16, 0xe1, 0x30, 0x71, 0x09, 0xf0, 0xe9, 0xf0, 0xe3, + 0xfe, 0x15, 0x64, 0x2e, 0x5c, 0xf7, 0x68, 0x81, 0xc9, 0xc4, 0x6b, 0x48, 0xf5, 0x8b, 0x79, 0x94, + 0x9f, 0xbc, 0x4b, 0x1a, 0x87, 0xbc, 0x6b, 0x84, 0x83, 0xcd, 0xfc, 0x1e, 0x0f, 0x36, 0x77, 0xe3, + 0x53, 0xf1, 0x72, 0xd8, 0x9f, 0x05, 0xb9, 0xfa, 0x48, 0xcf, 0x47, 0xab, 0xf7, 0x7b, 0xfa, 0xf0, + 0xb9, 0xe4, 0xfc, 0x93, 0x1c, 0xca, 0xd5, 0x26, 0xaa, 0x47, 0x7a, 0x73, 0xa0, 0x7a, 0xd3, 0xfd, + 0xce, 0x5a, 0x0d, 0xae, 0xa1, 0xfa, 0x43, 0x2f, 0xd1, 0xc8, 0x8d, 0xd3, 0x37, 0x72, 0x68, 0xa4, + 0x36, 0xb5, 0x50, 0x15, 0x4e, 0x82, 0x6f, 0x30, 0x4f, 0x3e, 0xf0, 0x29, 0x63, 0x5d, 0x7a, 0x36, + 0x66, 0xcf, 0xdc, 0xac, 0x58, 0xde, 0x33, 0xd7, 0x6e, 0xe9, 0xcd, 0x0e, 0x81, 0xa3, 0x17, 0xe6, + 0xf7, 0xeb, 0x9a, 0x6f, 0x92, 0xcf, 0xc3, 0xc3, 0x7f, 0x9f, 0x01, 0x7e, 0x01, 0xe5, 0x6e, 0x72, + 0x8f, 0x8c, 0x34, 0x3e, 0x4f, 0x5d, 0x65, 0x7c, 0xe8, 0x24, 0x98, 0xeb, 0x98, 0x06, 0x70, 0xa0, + 0xa5, 0x68, 0xe1, 0xeb, 0x7c, 0x01, 0xde, 0x56, 0xe1, 0x65, 0xbf, 0xf0, 0xf5, 0x4a, 0x19, 0xd7, + 0xd0, 0x60, 0x95, 0x38, 0x2d, 0x13, 0x3a, 0xca, 0x9f, 0xb3, 0xbb, 0x33, 0xa1, 0x3b, 0x95, 0xc1, + 0x76, 0x58, 0x08, 0x98, 0x89, 0x5c, 0xf0, 0xeb, 0x08, 0x31, 0x1b, 0x65, 0x9b, 0x81, 0x1c, 0xcf, + 0x81, 0xdd, 0xcf, 0x4c, 0xcb, 0x04, 0x1b, 0x4f, 0x60, 0x86, 0x57, 0xd1, 0xe8, 0xac, 0x6d, 0x98, + 0x4b, 0x26, 0x73, 0xbd, 0x84, 0x0a, 0x0a, 0x5b, 0x3b, 0x3c, 0x51, 0x53, 0xb2, 0x25, 0x94, 0x4b, + 0xaa, 0x26, 0xc6, 0x58, 0xfd, 0x87, 0xbd, 0x28, 0x4f, 0xbb, 0xfd, 0x68, 0xfc, 0xee, 0x65, 0xfc, + 0x96, 0xd0, 0xe8, 0x6d, 0xdb, 0x59, 0x35, 0xad, 0xe5, 0xc0, 0x2b, 0x9e, 0xef, 0x4d, 0xc1, 0x93, + 0x67, 0x8d, 0xe1, 0xea, 0x81, 0x03, 0xbd, 0x16, 0x23, 0xdf, 0x62, 0x04, 0x3f, 0x87, 0x10, 0x7b, + 0xeb, 0x0e, 0x34, 0xfd, 0x61, 0xb0, 0x0a, 0xf6, 0x12, 0x1e, 0x1c, 0xed, 0xc5, 0x60, 0x15, 0x21, + 0x31, 0xdd, 0x84, 0x33, 0x5f, 0x88, 0x01, 0xf0, 0xbb, 0x87, 0x4d, 0x38, 0xf8, 0x42, 0x88, 0x46, + 0x00, 0xf3, 0x8a, 0xa8, 0x22, 0x24, 0xdc, 0x2f, 0xa1, 0x88, 0x20, 0xa4, 0xc9, 0x81, 0x87, 0x87, + 0x4b, 0xb8, 0x5e, 0xd2, 0x04, 0x1e, 0xf8, 0x99, 0xc8, 0x05, 0x38, 0x96, 0xb8, 0xa5, 0xde, 0x7f, + 0x87, 0x0e, 0x54, 0x43, 0x5b, 0x39, 0x50, 0xa9, 0x9f, 0xca, 0xa2, 0x81, 0x5a, 0x67, 0xd1, 0x5d, + 0x77, 0x3d, 0xd2, 0x3a, 0xe4, 0x6a, 0xec, 0x6f, 0xaf, 0xf2, 0x89, 0xdb, 0xab, 0x87, 0x7c, 0xa1, + 0x08, 0xe7, 0x8e, 0x81, 0x49, 0xe7, 0x8b, 0xe3, 0x97, 0xb3, 0x68, 0x94, 0x5d, 0x9c, 0x95, 0x4d, + 0xb7, 0xb1, 0x0f, 0xce, 0xfc, 0x07, 0x2f, 0x95, 0xbd, 0x5d, 0x36, 0x6f, 0xe3, 0x89, 0x84, 0xfa, + 0xf1, 0x2c, 0x1a, 0x2c, 0x75, 0xbc, 0x95, 0x92, 0x07, 0xba, 0x75, 0x4f, 0xee, 0x4f, 0x7e, 0x3b, + 0x83, 0x8e, 0xd1, 0x86, 0x2c, 0xd8, 0xab, 0xc4, 0xda, 0x87, 0x83, 0x47, 0xf1, 0x00, 0x31, 0xbb, + 0xcb, 0x03, 0x44, 0x5f, 0x96, 0xb9, 0x9d, 0xc9, 0x12, 0x8e, 0xcb, 0x35, 0xbb, 0x49, 0x0e, 0xf7, + 0x67, 0xec, 0xe3, 0x71, 0xb9, 0x2f, 0x90, 0x7d, 0xb8, 0x9e, 0x79, 0x77, 0x09, 0x64, 0x1f, 0xce, + 0x96, 0xde, 0x1d, 0x02, 0xf9, 0x4a, 0x06, 0x0d, 0x8c, 0xdb, 0xde, 0x21, 0x1f, 0xf8, 0xfc, 0x2b, + 0x0e, 0xb7, 0x9a, 0xfb, 0x5f, 0x71, 0xb8, 0x75, 0x53, 0xfd, 0xa1, 0x2c, 0x3a, 0xc1, 0x83, 0x74, + 0xf3, 0xf3, 0x87, 0xa3, 0xe9, 0x98, 0x0f, 0xb6, 0xb8, 0x68, 0x8e, 0xe6, 0x21, 0x2e, 0x9a, 0x9f, + 0xce, 0xa1, 0x13, 0x10, 0xca, 0x94, 0x6e, 0xcb, 0xde, 0x05, 0xb6, 0x08, 0x6e, 0xc8, 0x97, 0xa0, + 0xb3, 0x09, 0x97, 0xa0, 0x7f, 0xb9, 0x51, 0x7c, 0x66, 0xd9, 0xf4, 0x56, 0x3a, 0x8b, 0x97, 0x1b, + 0x76, 0xeb, 0xca, 0xb2, 0xa3, 0xdf, 0x31, 0xd9, 0xf5, 0x9f, 0xde, 0xbc, 0x12, 0xe4, 0xbb, 0xd0, + 0xdb, 0x26, 0xcf, 0x84, 0x51, 0x83, 0xbd, 0x0e, 0xe5, 0xea, 0x5f, 0x9f, 0xba, 0x08, 0xbd, 0x6a, + 0x9b, 0x16, 0xf7, 0x29, 0x64, 0x86, 0x6e, 0x8d, 0xee, 0x0f, 0xdf, 0xb0, 0x4d, 0xab, 0x1e, 0x75, + 0x2c, 0xdc, 0x69, 0x7d, 0x21, 0x6b, 0x4d, 0xa8, 0x46, 0xfd, 0x17, 0x19, 0x74, 0x5a, 0xd6, 0xe2, + 0x77, 0x83, 0xed, 0xf8, 0xc3, 0x59, 0x74, 0xdf, 0x75, 0x10, 0x4e, 0xe0, 0xc8, 0x71, 0x34, 0x6f, + 0xf1, 0xc1, 0x99, 0x20, 0x9b, 0x23, 0x8b, 0x32, 0x5d, 0x36, 0x47, 0x93, 0x3a, 0x97, 0xcd, 0x3f, + 0xcf, 0xa0, 0xe3, 0xf3, 0x95, 0xf2, 0xc4, 0xbb, 0x64, 0x44, 0xc5, 0xbf, 0xe7, 0x90, 0x1b, 0x9c, + 0xb1, 0xef, 0x39, 0xe4, 0xa6, 0x27, 0xfd, 0x9e, 0x5a, 0x69, 0x76, 0xe6, 0xdd, 0xa4, 0x6f, 0xd2, + 0xf7, 0xbc, 0x0b, 0xf4, 0x4d, 0xfa, 0x9e, 0x43, 0xae, 0x6f, 0xff, 0xb8, 0x80, 0x06, 0x6f, 0x74, + 0x16, 0x09, 0x77, 0x09, 0xb9, 0xa7, 0xcf, 0x5b, 0xaf, 0xa2, 0x41, 0x2e, 0x06, 0xb8, 0xab, 0x10, + 0x42, 0xd6, 0xf1, 0x10, 0x24, 0x2c, 0x2a, 0x90, 0x48, 0x84, 0xcf, 0xa2, 0xfc, 0x2d, 0xe2, 0x2c, + 0x8a, 0xaf, 0x39, 0xef, 0x10, 0x67, 0x51, 0x03, 0x28, 0x9e, 0x09, 0x1d, 0xd5, 0x4b, 0xd5, 0x0a, + 0xa4, 0x2f, 0xe1, 0xd7, 0x24, 0x90, 0x8f, 0x25, 0xf0, 0x36, 0xd3, 0xdb, 0x26, 0x4b, 0x7c, 0x22, + 0xbe, 0x24, 0x8f, 0x96, 0xc4, 0x73, 0x68, 0x4c, 0x74, 0x37, 0x62, 0xb9, 0x3b, 0xfa, 0x13, 0xd8, + 0x25, 0x65, 0xed, 0x88, 0x17, 0xc5, 0x2f, 0xa3, 0x21, 0x1f, 0x08, 0x8e, 0x53, 0x03, 0x61, 0xc0, + 0xf8, 0x80, 0x55, 0x24, 0x31, 0x90, 0x54, 0x40, 0x64, 0x00, 0x87, 0xff, 0x28, 0x81, 0x41, 0xc4, + 0x11, 0x4d, 0x2a, 0x80, 0x9f, 0x06, 0x06, 0xf0, 0xb8, 0x02, 0x5c, 0x44, 0x06, 0xe1, 0xa9, 0x23, + 0x38, 0xc2, 0x3b, 0x1c, 0xce, 0x1e, 0xb4, 0x4a, 0x64, 0x78, 0x1e, 0xa1, 0xf0, 0x2a, 0x9f, 0x87, + 0x0d, 0xd8, 0xb1, 0x93, 0x81, 0xc0, 0x42, 0xbc, 0x84, 0x1b, 0xde, 0xcd, 0x25, 0x9c, 0xfa, 0xfb, + 0x59, 0x34, 0x58, 0x6a, 0xb7, 0x83, 0xa1, 0xf0, 0x38, 0x2a, 0x94, 0xda, 0xed, 0x9b, 0x5a, 0x45, + 0x0c, 0x20, 0xae, 0xb7, 0xdb, 0xf5, 0x8e, 0x63, 0x8a, 0x9e, 0x98, 0x8c, 0x08, 0x4f, 0xa0, 0xe1, + 0x52, 0xbb, 0x5d, 0xed, 0x2c, 0x36, 0xcd, 0x86, 0x90, 0x8f, 0x88, 0xa5, 0x4e, 0x6b, 0xb7, 0xeb, + 0x6d, 0xc0, 0x44, 0x93, 0x52, 0xc9, 0x65, 0xf0, 0x47, 0x21, 0xd8, 0x0e, 0x4f, 0x87, 0xc3, 0x12, + 0x6e, 0xa8, 0x41, 0xe8, 0xf0, 0xb0, 0x6d, 0x97, 0x03, 0x22, 0x16, 0x62, 0xfd, 0xac, 0x1f, 0xa8, + 0x9e, 0x56, 0x14, 0x4b, 0x7b, 0x13, 0xb2, 0xc4, 0x4f, 0xa0, 0xbe, 0x52, 0xbb, 0x2d, 0xdc, 0xf2, + 0x80, 0x2b, 0x0f, 0x2d, 0x15, 0xe9, 0x63, 0x9f, 0xec, 0xcc, 0x8b, 0x68, 0x44, 0xae, 0x6c, 0x47, + 0x21, 0xda, 0xbf, 0x99, 0x81, 0x0f, 0x3a, 0xe4, 0x9e, 0xc4, 0x4f, 0xa1, 0x5c, 0xa9, 0xdd, 0xe6, + 0xf3, 0xd1, 0xf1, 0x84, 0xfe, 0x88, 0x3e, 0x3c, 0x2e, 0xb5, 0xdb, 0xfe, 0xa7, 0x1f, 0xf2, 0x27, + 0x09, 0xbb, 0xfa, 0xf4, 0xaf, 0xb0, 0x4f, 0x3f, 0xdc, 0xcf, 0x05, 0xd4, 0x2f, 0xe6, 0xd0, 0xb1, + 0x52, 0xbb, 0x7d, 0x14, 0xda, 0x7d, 0xbf, 0x9e, 0x37, 0x3f, 0x89, 0x90, 0x30, 0x3d, 0xf6, 0x05, + 0x0f, 0xa6, 0x06, 0x85, 0xa9, 0x51, 0xc9, 0x68, 0x02, 0x91, 0xaf, 0x7e, 0xfd, 0x3b, 0x52, 0xbf, + 0x8f, 0xe7, 0x60, 0x2a, 0x3e, 0xec, 0xa1, 0x9a, 0xde, 0x29, 0xdd, 0xc6, 0xfb, 0xa0, 0xb0, 0xa3, + 0x3e, 0xf8, 0x2d, 0x69, 0xf0, 0x40, 0xa8, 0xf0, 0xa3, 0x5e, 0xe8, 0xdd, 0x93, 0x59, 0x3c, 0x22, + 0x0a, 0x93, 0xc7, 0x8f, 0xf1, 0xd3, 0x17, 0xf1, 0x68, 0x46, 0x0d, 0x8a, 0xaa, 0x9b, 0x86, 0x16, + 0xa1, 0xf5, 0xfb, 0xb0, 0x6f, 0x47, 0x7d, 0xb8, 0x91, 0x85, 0x17, 0xcb, 0x41, 0x34, 0xa4, 0xbd, + 0xef, 0x2e, 0xae, 0x20, 0xc4, 0xee, 0xfb, 0x03, 0x67, 0xe2, 0x61, 0x16, 0xf8, 0x84, 0x65, 0x35, + 0xe2, 0x81, 0x4f, 0x42, 0x92, 0xc0, 0x2f, 0x29, 0x97, 0xe8, 0x97, 0x74, 0x09, 0xf5, 0x6b, 0xfa, + 0xda, 0x6b, 0x1d, 0xe2, 0xac, 0x73, 0x73, 0x86, 0x05, 0x1b, 0xd4, 0xd7, 0xea, 0x1f, 0xa3, 0x40, + 0x2d, 0x40, 0x63, 0x35, 0x78, 0xf2, 0x2e, 0xf8, 0x61, 0xb0, 0x93, 0xe9, 0xe0, 0xa1, 0xfb, 0x6e, + 0x14, 0x1d, 0x3f, 0x8f, 0x72, 0xa5, 0xdb, 0x35, 0x2e, 0xd9, 0xa0, 0x6b, 0x4b, 0xb7, 0x6b, 0x5c, + 0x5e, 0xa9, 0x65, 0x6f, 0xd7, 0xd4, 0x8f, 0x67, 0x11, 0x8e, 0x53, 0xe2, 0x67, 0xd0, 0x00, 0x40, + 0x97, 0xa9, 0xce, 0x88, 0xe9, 0x30, 0xd7, 0xdc, 0xba, 0x03, 0x50, 0xc9, 0xb8, 0xf3, 0x49, 0xf1, + 0x73, 0x90, 0xf9, 0x97, 0x27, 0x64, 0x93, 0xd2, 0x61, 0xae, 0xb9, 0x7e, 0xae, 0xdc, 0x48, 0xe2, + 0x5f, 0x4e, 0x0c, 0x76, 0xe1, 0xed, 0xda, 0xb4, 0xed, 0x7a, 0x5c, 0xd4, 0xcc, 0x2e, 0x5c, 0x73, + 0x21, 0x0f, 0xab, 0x64, 0x17, 0x32, 0x32, 0xc8, 0x25, 0x75, 0xbb, 0xc6, 0x1e, 0x87, 0x18, 0x9a, + 0xdd, 0xf4, 0x0d, 0x4a, 0x96, 0x4b, 0x6a, 0xcd, 0xad, 0xb3, 0x87, 0x25, 0x06, 0xa4, 0x1c, 0x96, + 0x72, 0x49, 0x49, 0xa5, 0xd4, 0x4f, 0xf7, 0xa3, 0xd1, 0xb2, 0xee, 0xe9, 0x8b, 0xba, 0x4b, 0x84, + 0xdd, 0xf4, 0x31, 0x1f, 0xe6, 0x7f, 0x8e, 0x20, 0x07, 0x63, 0x31, 0xe1, 0x6b, 0xa2, 0x05, 0xf0, + 0x0b, 0x21, 0xdf, 0x20, 0xd3, 0xa7, 0x98, 0x3a, 0x6c, 0xb1, 0xde, 0xe6, 0x60, 0x2d, 0x46, 0x88, + 0x1f, 0x43, 0x83, 0x3e, 0x8c, 0x6e, 0x00, 0x72, 0xa1, 0xce, 0x18, 0x8b, 0xd4, 0xfe, 0xd7, 0x44, + 0x34, 0x7e, 0x0e, 0x0d, 0xf9, 0x3f, 0x05, 0xd3, 0x9a, 0xe5, 0x41, 0x5b, 0x8c, 0xed, 0x9e, 0x44, + 0x52, 0xb1, 0x28, 0xcc, 0x6f, 0xbd, 0x52, 0xd1, 0x48, 0xaa, 0x31, 0x89, 0x14, 0x7f, 0x0c, 0x8d, + 0xf8, 0xbf, 0xf9, 0x86, 0x81, 0x65, 0x65, 0x7b, 0x2c, 0xc8, 0x68, 0x1c, 0x11, 0xeb, 0x65, 0x99, + 0x9c, 0x6d, 0x1d, 0xee, 0xf7, 0xb3, 0x67, 0x19, 0x8b, 0xf1, 0x9d, 0x43, 0xa4, 0x02, 0x5c, 0x41, + 0x63, 0x3e, 0x24, 0xd4, 0xd0, 0xbe, 0x70, 0xc7, 0x68, 0x2c, 0xd6, 0x13, 0x95, 0x34, 0x5e, 0x0a, + 0x37, 0xd1, 0x59, 0x09, 0x68, 0xb8, 0x2b, 0xe6, 0x92, 0xc7, 0xb7, 0x7b, 0x3c, 0xf2, 0x2f, 0x4f, + 0x97, 0x18, 0x70, 0x65, 0x34, 0x7e, 0xde, 0x53, 0x39, 0x27, 0x53, 0x57, 0x6e, 0xb8, 0x86, 0x4e, + 0xf8, 0xf8, 0xeb, 0x13, 0xd5, 0xaa, 0x63, 0xbf, 0x41, 0x1a, 0x5e, 0xa5, 0xcc, 0xb7, 0xcb, 0x10, + 0x11, 0xce, 0x58, 0xac, 0x2f, 0x37, 0xda, 0x54, 0x29, 0x28, 0x4e, 0x66, 0x9e, 0x58, 0x18, 0xdf, + 0x42, 0xf7, 0x09, 0xf0, 0x8a, 0xe5, 0x7a, 0xba, 0xd5, 0x20, 0x95, 0x32, 0xdf, 0x43, 0xc3, 0x7e, + 0x9e, 0x73, 0x35, 0x39, 0x52, 0x66, 0x9b, 0x5c, 0x1c, 0xbf, 0x88, 0x86, 0x7d, 0x04, 0xbb, 0xbb, + 0x1b, 0x84, 0xbb, 0x3b, 0x18, 0x92, 0xc6, 0x62, 0x3d, 0xfa, 0x86, 0x51, 0x26, 0x16, 0x35, 0x0a, + 0x12, 0xca, 0x0f, 0x49, 0x1a, 0xe5, 0xad, 0xb7, 0x13, 0x95, 0x11, 0x92, 0xcc, 0xbf, 0x1c, 0x6a, + 0xd4, 0xbc, 0x63, 0x2e, 0x9b, 0x6c, 0x27, 0xed, 0x3f, 0x5b, 0x5c, 0xac, 0xdb, 0x00, 0x4c, 0xd2, + 0x0f, 0x46, 0x7e, 0xa6, 0x84, 0x8e, 0x27, 0xe8, 0xd8, 0x8e, 0x76, 0x8c, 0x9f, 0xca, 0x86, 0x8d, + 0x38, 0xe4, 0xdb, 0xc6, 0x71, 0xd4, 0xef, 0x7f, 0x09, 0x37, 0x1e, 0x94, 0xb4, 0xa1, 0x19, 0xe5, + 0xe1, 0xe3, 0x25, 0x71, 0x1c, 0xf2, 0xad, 0xe4, 0x7e, 0x88, 0xe3, 0xad, 0x4c, 0x28, 0x8e, 0x43, + 0xbe, 0xbd, 0xfc, 0x9e, 0x7c, 0x38, 0x27, 0x1d, 0xed, 0x31, 0xf7, 0xcb, 0x4c, 0x0e, 0xbd, 0x4f, + 0x0b, 0x3b, 0x78, 0x3e, 0x28, 0xaa, 0x66, 0xdf, 0xee, 0x54, 0x13, 0xbf, 0x88, 0x06, 0xab, 0xb6, + 0xeb, 0x2d, 0x3b, 0xc4, 0xad, 0x06, 0x91, 0xeb, 0xe1, 0xe9, 0x69, 0x9b, 0x83, 0xeb, 0x6d, 0x69, + 0xf6, 0x17, 0xc9, 0xd5, 0x3f, 0xcc, 0xc5, 0xb4, 0x81, 0x19, 0xae, 0x87, 0x52, 0x1b, 0xf6, 0x61, + 0xa8, 0xe3, 0xab, 0xe1, 0x2a, 0xc8, 0x2c, 0xfc, 0x5e, 0x21, 0x2c, 0xdf, 0x22, 0x37, 0xf0, 0x65, + 0x12, 0xfc, 0x21, 0x74, 0x4a, 0x02, 0x54, 0x75, 0x47, 0x6f, 0x11, 0x2f, 0xcc, 0x12, 0x08, 0x81, + 0x96, 0xfc, 0xd2, 0xf5, 0x76, 0x80, 0x16, 0x33, 0x0f, 0xa6, 0x70, 0x10, 0x54, 0xab, 0x6f, 0x07, + 0x8e, 0xcd, 0x7f, 0x94, 0x47, 0x4a, 0x60, 0x5e, 0x06, 0x4f, 0x78, 0x0e, 0x70, 0x2a, 0x7f, 0x47, + 0x74, 0xae, 0x89, 0xc6, 0x42, 0x61, 0xd4, 0x3a, 0xad, 0x96, 0x0e, 0x1d, 0x4c, 0xcd, 0xd7, 0x62, + 0x94, 0x59, 0x48, 0xc8, 0x2c, 0xd6, 0x33, 0xdc, 0x62, 0xc5, 0xe1, 0x13, 0xa9, 0xba, 0xcb, 0x58, + 0x68, 0x71, 0xae, 0xf8, 0x33, 0x19, 0x74, 0xa2, 0xb4, 0xb4, 0x44, 0x1a, 0x1e, 0x31, 0xe6, 0x17, + 0xa9, 0xe9, 0x36, 0x61, 0x77, 0x2c, 0xcf, 0xb7, 0x96, 0x9f, 0x4f, 0xaf, 0x8e, 0x75, 0xd2, 0xe5, + 0xa4, 0xc2, 0xac, 0x25, 0x41, 0xc8, 0x01, 0x9d, 0x93, 0xd4, 0x6d, 0xa0, 0xa9, 0x37, 0x80, 0x48, + 0x4b, 0xac, 0xf7, 0xcc, 0x75, 0x74, 0x3a, 0x95, 0xe5, 0x56, 0xa6, 0x52, 0xaf, 0x68, 0x2a, 0xfd, + 0xab, 0x4c, 0xa8, 0xee, 0x11, 0x21, 0xe1, 0xcb, 0x08, 0x85, 0x20, 0xbe, 0x79, 0x1a, 0xd9, 0xdc, + 0x28, 0xa2, 0x50, 0x68, 0x9a, 0x40, 0x81, 0xe7, 0x51, 0x81, 0x8b, 0x85, 0xe5, 0x7d, 0x7d, 0xef, + 0x16, 0xbd, 0x70, 0x59, 0x94, 0x03, 0x6c, 0x8c, 0xf8, 0x37, 0x73, 0x36, 0x67, 0x9e, 0x43, 0x83, + 0xbb, 0xfd, 0xae, 0xcf, 0xe4, 0x10, 0x16, 0x77, 0x3a, 0x07, 0x68, 0x06, 0xbe, 0x23, 0x06, 0xcb, + 0xee, 0xf2, 0xb6, 0x5c, 0x44, 0xfd, 0xf4, 0x13, 0x20, 0x13, 0x82, 0x10, 0xf9, 0xb4, 0xc3, 0x61, + 0x5a, 0x80, 0x0d, 0xc3, 0x0e, 0xf5, 0x25, 0x87, 0x1d, 0x52, 0xbf, 0x3f, 0x87, 0x4e, 0x8a, 0x1d, + 0x52, 0x26, 0x10, 0x4c, 0xfd, 0xa8, 0x53, 0xde, 0xc6, 0x4e, 0x51, 0x51, 0x81, 0x19, 0xb8, 0x3c, + 0xaa, 0x3d, 0x3b, 0x7c, 0x00, 0x88, 0xc6, 0x31, 0xea, 0xbf, 0xcf, 0xa2, 0xe1, 0xc0, 0x88, 0xd0, + 0x1d, 0xf7, 0x1e, 0xee, 0x8e, 0xf7, 0xa1, 0x61, 0x08, 0x1c, 0xd3, 0x22, 0x16, 0x0b, 0xae, 0xd2, + 0x2b, 0xa4, 0xa1, 0xf0, 0x11, 0x3c, 0xe3, 0x90, 0x44, 0x48, 0xb5, 0x9f, 0xd9, 0x17, 0x42, 0x38, + 0x1f, 0x66, 0x5c, 0x30, 0xb8, 0xfa, 0x13, 0x39, 0x34, 0xe4, 0x4b, 0x79, 0xdc, 0x3c, 0xac, 0xb7, + 0x09, 0x07, 0x2b, 0xe4, 0x2b, 0x08, 0x55, 0x6d, 0xc7, 0xd3, 0x9b, 0x73, 0xa1, 0xe6, 0xc3, 0x31, + 0x5c, 0x1b, 0xa0, 0xac, 0x8c, 0x40, 0x02, 0xeb, 0x57, 0x68, 0xbc, 0xb1, 0x89, 0x89, 0xad, 0x5f, + 0x01, 0x54, 0x13, 0x28, 0xd4, 0xdf, 0xc8, 0xa2, 0x63, 0x7e, 0x27, 0x4d, 0xde, 0x25, 0x8d, 0xce, + 0xbd, 0x3c, 0x37, 0xc9, 0xd2, 0xee, 0xdd, 0x52, 0xda, 0xea, 0x7f, 0x15, 0x26, 0x92, 0x89, 0xa6, + 0x7d, 0x34, 0x91, 0xfc, 0x75, 0xe8, 0xb8, 0xfa, 0xed, 0x39, 0x74, 0xc2, 0x97, 0xfa, 0x54, 0xc7, + 0x82, 0x0d, 0xec, 0x84, 0xde, 0x6c, 0xde, 0xcb, 0x7b, 0xbe, 0x41, 0x5f, 0x10, 0xf3, 0x3c, 0x12, + 0x1b, 0xcf, 0xfe, 0xb6, 0xc4, 0xc1, 0x75, 0xdb, 0x34, 0x34, 0x91, 0x08, 0xbf, 0x8c, 0x86, 0xfc, + 0x9f, 0x25, 0x67, 0xd9, 0xdf, 0xe8, 0xc1, 0x71, 0x74, 0x50, 0x48, 0x77, 0xa4, 0x07, 0xe7, 0x52, + 0x01, 0xf5, 0x3f, 0x16, 0xd0, 0x99, 0xdb, 0xa6, 0x65, 0xd8, 0x6b, 0xae, 0x9f, 0x3c, 0xf0, 0xd0, + 0x1f, 0xc7, 0x1c, 0x74, 0xd2, 0xc0, 0xd7, 0xd0, 0x7d, 0x51, 0x91, 0x3a, 0x41, 0x48, 0x67, 0xde, + 0x3b, 0x6b, 0x8c, 0xa0, 0xee, 0xa7, 0x11, 0xe4, 0x77, 0x3a, 0x5a, 0x72, 0xc9, 0x68, 0x1e, 0xc2, + 0xbe, 0xed, 0xe4, 0x21, 0x7c, 0x14, 0x15, 0xca, 0x76, 0x4b, 0x37, 0xfd, 0xd0, 0x23, 0x30, 0x8a, + 0x83, 0x7a, 0x01, 0xa3, 0x71, 0x0a, 0xca, 0x9f, 0x57, 0x0c, 0x5d, 0x36, 0x10, 0xf2, 0xf7, 0x0b, + 0x50, 0x2b, 0x4d, 0x13, 0x89, 0xb0, 0x8d, 0x86, 0x79, 0x75, 0xfc, 0x06, 0x06, 0xc1, 0xe6, 0xe9, + 0x69, 0x5f, 0x46, 0xe9, 0x6a, 0x75, 0x59, 0x2a, 0xc7, 0xb6, 0x51, 0x2c, 0x3d, 0x22, 0xff, 0x18, + 0x76, 0x17, 0xa3, 0xc9, 0xfc, 0x05, 0x21, 0xc0, 0x24, 0x33, 0x18, 0x17, 0x02, 0xcc, 0x32, 0x22, + 0x11, 0x9e, 0x44, 0x63, 0x10, 0x78, 0x37, 0xd8, 0x4a, 0x51, 0x95, 0x18, 0x02, 0xa3, 0x12, 0x0e, + 0xf6, 0x59, 0xac, 0x5e, 0xfa, 0x71, 0xf5, 0x06, 0x47, 0x6b, 0xf1, 0x12, 0x67, 0x5e, 0x41, 0x38, + 0xde, 0xe6, 0x1d, 0x1d, 0xed, 0x7f, 0x3a, 0x1b, 0xee, 0xeb, 0x0e, 0xbb, 0x73, 0xc6, 0x7e, 0x1c, + 0x66, 0xff, 0x42, 0x06, 0x8d, 0xc5, 0x02, 0x39, 0xe3, 0xa7, 0x10, 0x62, 0x10, 0x21, 0x60, 0x1e, + 0x44, 0xa0, 0x08, 0x83, 0x3b, 0xf3, 0xa5, 0x24, 0x24, 0xc3, 0x57, 0x50, 0x3f, 0xfb, 0xc5, 0x83, + 0xdc, 0xc4, 0x8b, 0x74, 0x3a, 0xa6, 0xa1, 0x05, 0x44, 0x61, 0x2d, 0x70, 0x47, 0x94, 0x4b, 0x2c, + 0xe2, 0xad, 0xb7, 0x83, 0x5a, 0x28, 0x19, 0xed, 0xc0, 0xa1, 0xa0, 0xc1, 0x25, 0xe3, 0xa0, 0xba, + 0xae, 0xc0, 0x63, 0x62, 0xe7, 0xb6, 0x8a, 0x89, 0x1d, 0x99, 0x9b, 0x78, 0x10, 0xec, 0xfd, 0x7b, + 0x58, 0xf2, 0xd9, 0x2c, 0x3a, 0x16, 0xd4, 0x7a, 0x80, 0xd7, 0x11, 0xef, 0x20, 0x91, 0x7c, 0x26, + 0x83, 0x94, 0x71, 0xb3, 0xd9, 0x34, 0xad, 0xe5, 0x8a, 0xb5, 0x64, 0x3b, 0x2d, 0x98, 0x3c, 0x0e, + 0xee, 0xb8, 0x53, 0xfd, 0xae, 0x0c, 0x1a, 0xe3, 0x0d, 0x9a, 0xd0, 0x1d, 0xe3, 0xe0, 0xce, 0x92, + 0xa2, 0x2d, 0x39, 0x38, 0x7d, 0x51, 0xbf, 0x94, 0x45, 0x68, 0xc6, 0x6e, 0xac, 0x1e, 0xf2, 0x97, + 0x85, 0x2f, 0xa0, 0x02, 0x8b, 0x34, 0xc4, 0x35, 0x76, 0xec, 0x32, 0x7b, 0x30, 0x4a, 0x3f, 0x8d, + 0x21, 0xc6, 0x47, 0xf9, 0x09, 0x6d, 0x81, 0x45, 0x2a, 0x52, 0x32, 0x1a, 0x2f, 0x42, 0x2b, 0xa5, + 0x74, 0xdc, 0xaa, 0x09, 0x2a, 0xa5, 0x30, 0xb9, 0xd2, 0xcd, 0x8d, 0x62, 0xbe, 0x69, 0x37, 0x56, + 0x35, 0xa0, 0x57, 0xff, 0x2a, 0xc3, 0x64, 0x77, 0xc8, 0x5f, 0xd7, 0xf9, 0x9f, 0x9f, 0xdf, 0xe1, + 0xe7, 0x7f, 0x77, 0x06, 0x9d, 0xd0, 0x48, 0xc3, 0xbe, 0x43, 0x9c, 0xf5, 0x09, 0xdb, 0x20, 0xd7, + 0x89, 0x45, 0x9c, 0x83, 0x1a, 0x51, 0xff, 0x00, 0x92, 0x08, 0x84, 0x8d, 0xb9, 0xe9, 0x12, 0xe3, + 0xf0, 0x24, 0x78, 0x50, 0x7f, 0xa5, 0x0f, 0x29, 0x89, 0x16, 0xe2, 0xa1, 0xb5, 0x8a, 0x52, 0xcd, + 0xfe, 0xfc, 0x7e, 0x99, 0xfd, 0xbd, 0x3b, 0x33, 0xfb, 0x0b, 0x3b, 0x35, 0xfb, 0xfb, 0xb6, 0x63, + 0xf6, 0xb7, 0xa2, 0x66, 0x7f, 0x3f, 0x98, 0xfd, 0x4f, 0x75, 0x35, 0xfb, 0x27, 0x2d, 0x63, 0x97, + 0x46, 0xff, 0xa1, 0x4d, 0x3e, 0xba, 0x9b, 0xdd, 0xca, 0x45, 0x3a, 0x29, 0x36, 0x6c, 0xc7, 0x20, + 0x06, 0xdf, 0xa4, 0xc0, 0x09, 0xb9, 0xc3, 0x61, 0x5a, 0x80, 0x8d, 0x65, 0x72, 0x1d, 0xde, 0x4e, + 0x26, 0xd7, 0x7d, 0xd8, 0xc6, 0x7c, 0x2a, 0x8b, 0xc6, 0x26, 0x88, 0xe3, 0xb1, 0x50, 0x86, 0xfb, + 0xe1, 0xa4, 0x54, 0x42, 0xc7, 0x04, 0x86, 0x60, 0x91, 0x67, 0x43, 0xc7, 0xab, 0x06, 0x71, 0xbc, + 0xa8, 0xdf, 0x56, 0x94, 0x9e, 0x56, 0xef, 0x67, 0x53, 0xe2, 0x63, 0x37, 0xa8, 0xde, 0x87, 0x33, + 0x41, 0x9a, 0xfc, 0x97, 0x16, 0xd0, 0x0b, 0x09, 0x92, 0xf2, 0x3b, 0x4f, 0x90, 0xa4, 0xfe, 0x5c, + 0x06, 0x5d, 0xd0, 0x88, 0x45, 0xd6, 0xf4, 0xc5, 0x26, 0x11, 0x9a, 0xc5, 0x57, 0x06, 0x3a, 0x6b, + 0x98, 0x6e, 0x4b, 0xf7, 0x1a, 0x2b, 0x7b, 0x92, 0xd1, 0x14, 0x1a, 0x12, 0xe7, 0xaf, 0x1d, 0xcc, + 0x6d, 0x52, 0x39, 0xf5, 0xd7, 0x73, 0xa8, 0x6f, 0xdc, 0xf6, 0x5e, 0xb5, 0xf7, 0x98, 0xb1, 0x2b, + 0x9c, 0xf2, 0xb3, 0x3b, 0x38, 0x17, 0x79, 0x02, 0x2a, 0x17, 0x82, 0x98, 0x83, 0x53, 0xdf, 0xa2, + 0x1d, 0x0b, 0xf6, 0xee, 0x93, 0xed, 0x30, 0x57, 0xd7, 0x33, 0x68, 0x00, 0xa2, 0x60, 0x08, 0x27, + 0x97, 0xe0, 0x32, 0xeb, 0x51, 0x60, 0xb4, 0x8e, 0x90, 0x14, 0x7f, 0x48, 0x8a, 0xbd, 0x58, 0xd8, + 0x7b, 0x6e, 0x2f, 0x31, 0x0c, 0xe3, 0xbe, 0xa5, 0xd0, 0x52, 0xbf, 0x91, 0x47, 0x43, 0xbe, 0xa3, + 0xe4, 0x01, 0xf5, 0xe0, 0xe3, 0xa8, 0x30, 0x6d, 0x0b, 0x01, 0xd9, 0xc1, 0xb1, 0x72, 0xc5, 0x76, + 0x23, 0x1e, 0xa3, 0x9c, 0x08, 0x3f, 0x85, 0xfa, 0xe7, 0x6c, 0x43, 0x74, 0x0b, 0x86, 0x31, 0x6d, + 0xd9, 0x46, 0xec, 0x59, 0x65, 0x40, 0x88, 0x2f, 0xa0, 0x3c, 0x78, 0x54, 0x0b, 0x47, 0xcf, 0x11, + 0x2f, 0x6a, 0xc0, 0x0b, 0xba, 0x51, 0xd8, 0xa9, 0x6e, 0xf4, 0xed, 0x56, 0x37, 0xfa, 0xf7, 0x57, + 0x37, 0x5e, 0x47, 0x43, 0x50, 0x93, 0x9f, 0xcf, 0x69, 0xeb, 0xe5, 0xed, 0x34, 0x5f, 0x81, 0x86, + 0x59, 0xbb, 0x79, 0x56, 0x27, 0x58, 0x78, 0x24, 0x56, 0x11, 0xb5, 0x43, 0x7b, 0x50, 0xbb, 0x3f, + 0xcc, 0xa0, 0xbe, 0x9b, 0xd6, 0xaa, 0x65, 0xaf, 0xed, 0x4d, 0xe3, 0x9e, 0x42, 0x83, 0x9c, 0x8d, + 0x30, 0xc7, 0xc3, 0x4b, 0xd9, 0x0e, 0x03, 0xd7, 0x81, 0x93, 0x26, 0x52, 0xe1, 0x17, 0x83, 0x42, + 0xf0, 0x68, 0x22, 0x17, 0xa6, 0x34, 0xf0, 0x0b, 0x35, 0xe4, 0x28, 0xec, 0x22, 0x39, 0x3e, 0x8b, + 0xf2, 0x65, 0xda, 0x54, 0x21, 0xa6, 0x27, 0x6d, 0x8a, 0x06, 0x50, 0xf5, 0x9f, 0x65, 0xd1, 0x48, + 0xe4, 0xf8, 0xe9, 0x51, 0x34, 0xc0, 0x8f, 0x7f, 0x4c, 0x3f, 0x2c, 0x3c, 0x3c, 0xaa, 0x08, 0x80, + 0x5a, 0x3f, 0xfb, 0xb3, 0x62, 0xe0, 0xf7, 0xa3, 0x3e, 0xdb, 0x85, 0xa5, 0x09, 0xbe, 0x65, 0x24, + 0x1c, 0x42, 0xf3, 0x35, 0xda, 0x76, 0x36, 0x38, 0x38, 0x89, 0xa8, 0x91, 0xb6, 0x0b, 0x9f, 0x76, + 0x0d, 0x0d, 0xe8, 0xae, 0x4b, 0xbc, 0xba, 0xa7, 0x2f, 0x8b, 0x91, 0xe2, 0x03, 0xa0, 0x38, 0x3a, + 0x00, 0xb8, 0xa0, 0x2f, 0xe3, 0x57, 0xd0, 0x70, 0xc3, 0x21, 0xb0, 0x78, 0xe9, 0x4d, 0xda, 0x4a, + 0xc1, 0xb8, 0x94, 0x10, 0xe2, 0x89, 0x7f, 0x88, 0xa8, 0x18, 0xf8, 0x16, 0x1a, 0xe6, 0x9f, 0xc3, + 0x3c, 0x9a, 0x61, 0xa0, 0x8d, 0x84, 0x8b, 0x09, 0x13, 0x09, 0xf3, 0x69, 0xe6, 0x8e, 0xed, 0x22, + 0xb9, 0xc8, 0xd7, 0x10, 0x48, 0xd5, 0xaf, 0x66, 0xa8, 0xc1, 0x43, 0x01, 0x90, 0x61, 0x95, 0xea, + 0x4a, 0x6b, 0x87, 0xba, 0xd2, 0x0a, 0x73, 0xa1, 0x15, 0xdc, 0x2e, 0xb3, 0x93, 0xc6, 0xb1, 0xf8, + 0x32, 0x2a, 0x18, 0xe2, 0xd9, 0xcf, 0x49, 0xf9, 0x23, 0xfc, 0x7a, 0x34, 0x4e, 0x85, 0x2f, 0xa2, + 0x3c, 0x35, 0x68, 0xa3, 0x1b, 0x3f, 0x71, 0x8d, 0xd4, 0x80, 0x42, 0xfd, 0xd6, 0x2c, 0x1a, 0x12, + 0xbe, 0xe6, 0xea, 0x9e, 0x3e, 0xe7, 0xf9, 0xed, 0x35, 0xd3, 0x77, 0x73, 0x80, 0x1d, 0x81, 0xdf, + 0xe4, 0x6b, 0x81, 0x28, 0xb6, 0x75, 0x05, 0xc1, 0x05, 0xf3, 0x0c, 0xff, 0xd0, 0xc2, 0xf6, 0x37, + 0x41, 0x94, 0xfe, 0xd5, 0x7c, 0x7f, 0x76, 0x34, 0xf7, 0x6a, 0xbe, 0x3f, 0x3f, 0xda, 0x0b, 0x51, + 0x75, 0x20, 0x90, 0x2d, 0xdb, 0x61, 0x5a, 0x4b, 0xe6, 0xf2, 0x21, 0xf7, 0x49, 0xdf, 0xdf, 0x88, + 0x43, 0x11, 0xd9, 0x1c, 0x72, 0x07, 0xf5, 0xb7, 0x55, 0x36, 0x47, 0xb9, 0xd3, 0xb8, 0x6c, 0xfe, + 0x28, 0x83, 0x94, 0x44, 0xd9, 0x94, 0x0e, 0xe8, 0xe6, 0x7b, 0xff, 0x32, 0xa8, 0x7d, 0x3d, 0x8b, + 0xc6, 0x2a, 0x96, 0x47, 0x96, 0xd9, 0xbe, 0xe7, 0x90, 0x4f, 0x15, 0x37, 0xd0, 0xa0, 0xf0, 0x31, + 0xbc, 0xcf, 0xef, 0x0f, 0x76, 0x95, 0x21, 0x2a, 0x85, 0x93, 0x58, 0x7a, 0x1f, 0x93, 0x2e, 0x47, + 0x84, 0x7c, 0xc8, 0xe7, 0x9c, 0xc3, 0x21, 0xe4, 0x43, 0x3e, 0x79, 0xbd, 0x43, 0x85, 0xfc, 0x9f, + 0x33, 0xe8, 0x78, 0x42, 0xe5, 0xf8, 0x02, 0xea, 0xab, 0x75, 0x16, 0x21, 0x9c, 0x4f, 0x26, 0xf4, + 0x11, 0x75, 0x3b, 0x8b, 0x10, 0xc9, 0x47, 0xf3, 0x91, 0x78, 0x01, 0x1e, 0xed, 0xce, 0x57, 0xca, + 0x13, 0x5c, 0xaa, 0xaa, 0xf0, 0xfc, 0x98, 0x82, 0x93, 0xbe, 0x2c, 0x78, 0xd8, 0x6b, 0x9b, 0x46, + 0x23, 0xf2, 0xb0, 0x97, 0x96, 0xc1, 0x1f, 0x46, 0x03, 0xa5, 0x37, 0x3b, 0x0e, 0x01, 0xbe, 0x4c, + 0xe2, 0x0f, 0x07, 0x7c, 0x7d, 0x44, 0x12, 0x67, 0xf6, 0x46, 0x99, 0x52, 0x44, 0x79, 0x87, 0x0c, + 0xd5, 0x4f, 0x67, 0xd0, 0x99, 0xf4, 0xd6, 0xe1, 0x27, 0x50, 0x1f, 0xdd, 0xd9, 0x96, 0xb4, 0x39, + 0xfe, 0xe9, 0x2c, 0xdb, 0xa0, 0xdd, 0x24, 0x75, 0xdd, 0x11, 0x0d, 0x6f, 0x9f, 0x0c, 0xbf, 0x84, + 0x06, 0x2b, 0xae, 0xdb, 0x21, 0x4e, 0xed, 0xa9, 0x9b, 0x5a, 0x85, 0xef, 0xa9, 0xc0, 0x66, 0x37, + 0x01, 0x5c, 0x77, 0x9f, 0x8a, 0x04, 0xec, 0x11, 0xe9, 0xd5, 0x4f, 0x64, 0xd0, 0xd9, 0x6e, 0x5f, + 0x45, 0x37, 0xf0, 0x0b, 0xc4, 0xd2, 0x2d, 0xba, 0xe3, 0xcf, 0x84, 0x5b, 0x14, 0x0f, 0x60, 0xf2, + 0x26, 0x23, 0x20, 0xa4, 0x85, 0xd8, 0xe9, 0x58, 0x70, 0x1d, 0xcf, 0x4e, 0xf2, 0x00, 0x16, 0x29, + 0xe4, 0x13, 0xaa, 0xbf, 0x9b, 0x45, 0x43, 0xd5, 0x66, 0x67, 0xd9, 0x14, 0x16, 0x8e, 0x5d, 0xdb, + 0xdb, 0xbe, 0xf5, 0x9b, 0xdd, 0x99, 0xf5, 0x4b, 0x87, 0x9b, 0xb3, 0xcb, 0xe1, 0xe6, 0x97, 0xc3, + 0x2f, 0xa2, 0x42, 0x1b, 0xbe, 0x23, 0x7a, 0x9e, 0xc8, 0xbe, 0x2e, 0xed, 0x3c, 0x91, 0x95, 0xa1, + 0xe3, 0xab, 0xb1, 0x87, 0xf1, 0x15, 0x96, 0x15, 0x04, 0x1a, 0x2e, 0x12, 0x47, 0x02, 0xdd, 0x17, + 0x81, 0x86, 0x0b, 0xc2, 0x91, 0x40, 0xf7, 0x20, 0xd0, 0x5f, 0xc9, 0xa2, 0x11, 0xb9, 0x4a, 0xfc, + 0x04, 0x1a, 0x64, 0xd5, 0xb0, 0x73, 0x95, 0x8c, 0xe0, 0x46, 0x1a, 0x82, 0x35, 0xc4, 0x7e, 0xf0, + 0x03, 0xa2, 0x63, 0x2b, 0xba, 0x5b, 0x0f, 0x4f, 0x38, 0xd8, 0x2d, 0x64, 0x3f, 0xf3, 0xe7, 0x89, + 0xa0, 0xb4, 0x91, 0x15, 0xdd, 0x9d, 0x08, 0x7f, 0xe3, 0x49, 0x84, 0x21, 0x4d, 0xbb, 0xcc, 0x80, + 0xe5, 0xdd, 0xe7, 0xa9, 0x5e, 0xa3, 0x58, 0x6d, 0x8c, 0xc1, 0x44, 0x36, 0x1f, 0x09, 0x9a, 0x0d, + 0xca, 0xd0, 0xbb, 0x8d, 0x3c, 0xb4, 0x02, 0x7d, 0xf2, 0x31, 0x21, 0x23, 0x28, 0xeb, 0x9e, 0xce, + 0x36, 0xe5, 0x7e, 0x07, 0xa8, 0x7f, 0x66, 0xa2, 0xde, 0x79, 0x8b, 0xcc, 0x2f, 0xe1, 0x27, 0xd1, + 0x00, 0x55, 0x98, 0x19, 0x9b, 0xf6, 0x65, 0x86, 0x7b, 0x01, 0x08, 0x9a, 0x04, 0x88, 0xe9, 0x1e, + 0x2d, 0xa4, 0xc2, 0xd7, 0xc4, 0x4c, 0xec, 0x5c, 0xfb, 0xb0, 0x58, 0x86, 0x61, 0xa6, 0x7b, 0x34, + 0x31, 0x63, 0xfb, 0x35, 0x31, 0x03, 0x36, 0xd7, 0x3b, 0xa9, 0x14, 0xc3, 0xf8, 0xa5, 0xf8, 0xf8, + 0x98, 0x49, 0x4a, 0x13, 0x1d, 0xdd, 0x3d, 0xc5, 0x29, 0xa6, 0x7b, 0xb4, 0xe4, 0xf4, 0xd2, 0x43, + 0xa2, 0x0b, 0x61, 0xd4, 0x75, 0x41, 0xc4, 0x4d, 0xf7, 0x68, 0x12, 0x2d, 0x7e, 0x16, 0x0d, 0xf2, + 0xdf, 0xaf, 0xda, 0xa6, 0x15, 0x8d, 0x71, 0x22, 0xa0, 0xa6, 0x7b, 0x34, 0x91, 0x52, 0xa8, 0xb4, + 0xea, 0x98, 0x96, 0xc7, 0xdf, 0x3e, 0x46, 0x2b, 0x05, 0x9c, 0x50, 0x29, 0xfc, 0xc6, 0x2f, 0xa1, + 0xe1, 0x20, 0x78, 0xcc, 0x1b, 0xa4, 0xe1, 0xf1, 0xc3, 0xe3, 0xfb, 0x22, 0x85, 0x19, 0x72, 0xba, + 0x47, 0x93, 0xa9, 0xf1, 0x45, 0x54, 0xd0, 0x88, 0x6b, 0xbe, 0xe9, 0x5f, 0x7a, 0x8e, 0x08, 0xe3, + 0xdc, 0x7c, 0x93, 0x4a, 0x89, 0xe3, 0x69, 0xef, 0x84, 0xb7, 0xac, 0xfc, 0xa8, 0x17, 0x47, 0x6a, + 0x99, 0xb4, 0x0c, 0xda, 0x3b, 0xc2, 0x15, 0xfb, 0x2b, 0x61, 0x48, 0x1d, 0x9e, 0xfe, 0x6e, 0x30, + 0xfa, 0x76, 0x59, 0xc4, 0x4e, 0xf7, 0x68, 0x11, 0x7a, 0x41, 0xaa, 0x65, 0xd3, 0x5d, 0xe5, 0x51, + 0x0c, 0xa3, 0x52, 0xa5, 0x28, 0x41, 0xaa, 0xf4, 0xa7, 0x50, 0xf5, 0x1c, 0xf1, 0xd6, 0x6c, 0x67, + 0x95, 0xc7, 0x2c, 0x8c, 0x56, 0xcd, 0xb1, 0x42, 0xd5, 0x1c, 0x22, 0x56, 0x4d, 0x07, 0xdc, 0x48, + 0x72, 0xd5, 0xba, 0xa7, 0x8b, 0x55, 0xb3, 0x93, 0x38, 0xbf, 0x93, 0x66, 0x88, 0x7e, 0x87, 0x65, + 0x21, 0x8e, 0x77, 0x28, 0xe0, 0x84, 0x0e, 0x85, 0xdf, 0xb4, 0x52, 0x21, 0xd3, 0x2c, 0x4f, 0x33, + 0x1c, 0x54, 0x2a, 0xa0, 0x68, 0xa5, 0x62, 0x4e, 0xda, 0x6b, 0x62, 0x02, 0x56, 0x65, 0x4c, 0xee, + 0xa0, 0x10, 0x43, 0x3b, 0x48, 0x48, 0xd4, 0x5a, 0x84, 0xe4, 0x8e, 0x0a, 0x06, 0xf2, 0xc1, 0xa0, + 0x85, 0x13, 0xd5, 0xe9, 0x1e, 0x0d, 0xd2, 0x3e, 0xaa, 0x2c, 0x6d, 0xa8, 0x72, 0x1c, 0x28, 0x86, + 0x7c, 0x0a, 0x0a, 0x9b, 0xee, 0xd1, 0x58, 0x4a, 0xd1, 0x27, 0x85, 0x04, 0x5d, 0xca, 0x09, 0x79, + 0x8a, 0x08, 0x10, 0x74, 0x8a, 0x08, 0xd3, 0x78, 0x4d, 0xc5, 0x93, 0x58, 0x29, 0xf7, 0xc9, 0x4b, + 0x4d, 0x14, 0x3f, 0xdd, 0xa3, 0xc5, 0x13, 0x5f, 0x3d, 0x2b, 0xe5, 0x75, 0x52, 0x4e, 0x46, 0x02, + 0x0b, 0x85, 0x28, 0x2a, 0x2e, 0x31, 0x03, 0xd4, 0x7c, 0x62, 0x26, 0x76, 0xe5, 0x94, 0xbc, 0x71, + 0x49, 0x20, 0x99, 0xee, 0xd1, 0x12, 0x73, 0xb8, 0x4f, 0xc4, 0xb2, 0x2b, 0x29, 0x8a, 0xec, 0xe1, + 0x11, 0x41, 0x4f, 0xf7, 0x68, 0xb1, 0x7c, 0x4c, 0xd7, 0xc4, 0xb4, 0x46, 0xca, 0x69, 0xb9, 0x13, + 0x43, 0x0c, 0xed, 0x44, 0x21, 0xfd, 0xd1, 0x35, 0x31, 0xd5, 0x8d, 0x72, 0x26, 0x5e, 0x2a, 0x9c, + 0x39, 0x85, 0x94, 0x38, 0x5a, 0x72, 0xf6, 0x0e, 0xe5, 0x7e, 0x9e, 0x3f, 0x91, 0x97, 0x4f, 0xa2, + 0x99, 0xee, 0xd1, 0x92, 0x33, 0x7f, 0x68, 0xc9, 0x69, 0x2f, 0x94, 0xb3, 0xdd, 0x78, 0x06, 0xad, + 0x4b, 0x4e, 0x99, 0xa1, 0x77, 0x49, 0x42, 0xa0, 0x9c, 0x93, 0xa3, 0x9a, 0xa6, 0x12, 0x4e, 0xf7, + 0x68, 0x5d, 0x52, 0x19, 0xdc, 0x4c, 0xc9, 0x08, 0xa0, 0x9c, 0x97, 0xd3, 0xa7, 0x26, 0x12, 0x4d, + 0xf7, 0x68, 0x29, 0xf9, 0x04, 0x6e, 0xa6, 0x04, 0x8c, 0x57, 0x8a, 0x5d, 0xd9, 0x06, 0xf2, 0x48, + 0x09, 0x37, 0x3f, 0x9f, 0x18, 0x6b, 0x5d, 0x79, 0x40, 0x56, 0xdd, 0x04, 0x12, 0xaa, 0xba, 0x49, + 0x51, 0xda, 0xe7, 0x13, 0x83, 0x83, 0x2b, 0x0f, 0x76, 0x61, 0x18, 0xb4, 0x31, 0x31, 0xac, 0xf8, + 0x7c, 0x62, 0x74, 0x6e, 0x45, 0x95, 0x19, 0x26, 0x90, 0x50, 0x86, 0x49, 0x71, 0xbd, 0xe7, 0x13, + 0xc3, 0x49, 0x2b, 0x0f, 0x75, 0x61, 0x18, 0xb6, 0x30, 0x29, 0x10, 0xf5, 0xb3, 0x52, 0x3c, 0x67, + 0xe5, 0x61, 0x79, 0xde, 0x10, 0x50, 0x74, 0xde, 0x10, 0x23, 0x3f, 0x4f, 0xc4, 0x22, 0x56, 0x2a, + 0x8f, 0xc8, 0xc3, 0x3c, 0x82, 0xa6, 0xc3, 0x3c, 0x1a, 0xe3, 0x72, 0x22, 0x16, 0xb9, 0x4f, 0xb9, + 0x90, 0xc6, 0x04, 0xd0, 0x32, 0x13, 0x16, 0xeb, 0xaf, 0x92, 0x10, 0x3a, 0x4e, 0x79, 0x8f, 0xec, + 0x9d, 0x1c, 0x23, 0x98, 0xee, 0xd1, 0x12, 0x02, 0xce, 0x69, 0xc9, 0x71, 0x52, 0x94, 0x8b, 0xf2, + 0xb0, 0x4d, 0xa2, 0xa1, 0xc3, 0x36, 0x31, 0xc6, 0xca, 0x4c, 0xd2, 0x4b, 0x04, 0xe5, 0x92, 0x6c, + 0x98, 0xc5, 0x29, 0xa8, 0x61, 0x96, 0xf0, 0x82, 0x41, 0x4b, 0x8e, 0xdd, 0xa1, 0x3c, 0xda, 0xb5, + 0x85, 0x40, 0x93, 0xd0, 0x42, 0x16, 0xca, 0x22, 0xb4, 0x9d, 0x6e, 0xb6, 0x9b, 0xb6, 0x6e, 0x28, + 0xef, 0x4d, 0xb4, 0x9d, 0x18, 0x52, 0xb0, 0x9d, 0x18, 0x80, 0xae, 0xf2, 0xa2, 0xa7, 0xbe, 0xf2, + 0x98, 0xbc, 0xca, 0x8b, 0x38, 0xba, 0xca, 0x4b, 0x5e, 0xfd, 0x13, 0x31, 0xaf, 0x76, 0xe5, 0x71, + 0x59, 0x01, 0x22, 0x68, 0xaa, 0x00, 0x51, 0x3f, 0xf8, 0x8f, 0xa6, 0xfb, 0x81, 0x2b, 0x97, 0x81, + 0xdb, 0x03, 0x3e, 0xb7, 0x34, 0xba, 0xe9, 0x1e, 0x2d, 0xdd, 0x97, 0xbc, 0x92, 0xe0, 0xd6, 0xad, + 0x5c, 0x91, 0x15, 0x2c, 0x46, 0x40, 0x15, 0x2c, 0xee, 0x0c, 0x5e, 0x49, 0xf0, 0xcb, 0x56, 0x9e, + 0x48, 0x65, 0x15, 0x7c, 0x73, 0x82, 0x37, 0xf7, 0x35, 0xd1, 0xb1, 0x5a, 0x79, 0x52, 0x5e, 0xec, + 0x42, 0x0c, 0x5d, 0xec, 0x04, 0x07, 0xec, 0x6b, 0xa2, 0x4b, 0xb1, 0x72, 0x35, 0x5e, 0x2a, 0x5c, + 0x22, 0x05, 0xd7, 0x63, 0x2d, 0xd9, 0x13, 0x57, 0x79, 0x4a, 0xd6, 0xba, 0x24, 0x1a, 0xaa, 0x75, + 0x89, 0x5e, 0xbc, 0x53, 0x71, 0x87, 0x5a, 0xe5, 0x5a, 0x74, 0x93, 0x2d, 0xe3, 0xa9, 0xe5, 0x13, + 0x73, 0xc2, 0x7d, 0x25, 0x1a, 0xc4, 0x4b, 0x79, 0x3a, 0x72, 0xed, 0x2b, 0x61, 0xa9, 0x7d, 0x1b, + 0x09, 0xfa, 0xf5, 0x4a, 0x34, 0xee, 0x95, 0xf2, 0x4c, 0x32, 0x87, 0x40, 0x57, 0xa2, 0x71, 0xb2, + 0x5e, 0x89, 0x86, 0x8a, 0x52, 0x9e, 0x4d, 0xe6, 0x10, 0x48, 0x37, 0x1a, 0x5a, 0xea, 0x49, 0x21, + 0x78, 0xb5, 0xf2, 0x3e, 0xd9, 0x74, 0x0c, 0x10, 0xd4, 0x74, 0x0c, 0x43, 0x5c, 0x3f, 0x29, 0x04, + 0x7d, 0x56, 0x9e, 0x8b, 0x15, 0x09, 0x1a, 0x2b, 0x84, 0x86, 0x7e, 0x52, 0x08, 0x96, 0xac, 0x3c, + 0x1f, 0x2b, 0x12, 0xb4, 0x4e, 0x08, 0xa9, 0x6c, 0x74, 0x7b, 0xb1, 0xa8, 0xbc, 0x20, 0x1f, 0x06, + 0xa7, 0x53, 0x4e, 0xf7, 0x68, 0xdd, 0x5e, 0x3e, 0x7e, 0x34, 0xdd, 0x3d, 0x59, 0x79, 0x51, 0x1e, + 0xc2, 0x69, 0x74, 0x74, 0x08, 0xa7, 0xba, 0x38, 0xbf, 0x14, 0x89, 0x5e, 0xa0, 0xbc, 0x24, 0x4f, + 0x71, 0x12, 0x92, 0x4e, 0x71, 0xd1, 0x58, 0x07, 0xd2, 0xb3, 0x7c, 0xe5, 0xfd, 0xf2, 0x14, 0x27, + 0xe2, 0xe8, 0x14, 0x27, 0x3d, 0xe1, 0x9f, 0x88, 0xbd, 0x16, 0x57, 0x5e, 0x96, 0xa7, 0xb8, 0x08, + 0x9a, 0x4e, 0x71, 0xd1, 0xf7, 0xe5, 0x2f, 0x45, 0x1e, 0x4d, 0x2b, 0xaf, 0x24, 0xb7, 0x1f, 0x90, + 0x62, 0xfb, 0xd9, 0x13, 0x6b, 0x2d, 0xf9, 0xf5, 0xaf, 0x52, 0x92, 0xc7, 0x6f, 0x12, 0x0d, 0x1d, + 0xbf, 0x89, 0x2f, 0x87, 0xa3, 0x1b, 0x07, 0xae, 0x55, 0xe3, 0x5d, 0x36, 0x0e, 0xa1, 0x29, 0x92, + 0x00, 0x96, 0xf6, 0xc8, 0x6c, 0x23, 0x34, 0x91, 0xb2, 0x47, 0xf6, 0xb7, 0x41, 0x11, 0x7a, 0x3a, + 0xbb, 0xc6, 0xbc, 0x65, 0x95, 0xb2, 0x3c, 0xbb, 0xc6, 0x08, 0xe8, 0xec, 0x1a, 0xf7, 0xb1, 0x9d, + 0x42, 0xa3, 0x5c, 0x8b, 0x98, 0x13, 0xb0, 0x69, 0x2d, 0x2b, 0x93, 0x91, 0xc7, 0x77, 0x11, 0x3c, + 0x9d, 0x9d, 0xa2, 0x30, 0x58, 0xaf, 0x19, 0x6c, 0xa2, 0x69, 0xb6, 0x17, 0x6d, 0xdd, 0x31, 0x6a, + 0xc4, 0x32, 0x94, 0xa9, 0xc8, 0x7a, 0x9d, 0x40, 0x03, 0xeb, 0x75, 0x02, 0x1c, 0x42, 0x4f, 0x45, + 0xe0, 0x1a, 0x69, 0x10, 0xf3, 0x0e, 0x51, 0xae, 0x03, 0xdb, 0x62, 0x1a, 0x5b, 0x4e, 0x36, 0xdd, + 0xa3, 0xa5, 0x71, 0xa0, 0xb6, 0xfa, 0xec, 0x7a, 0xed, 0xb5, 0x99, 0xe0, 0xc1, 0x79, 0xd5, 0x21, + 0x6d, 0xdd, 0x21, 0xca, 0xb4, 0x6c, 0xab, 0x27, 0x12, 0x51, 0x5b, 0x3d, 0x11, 0x11, 0x67, 0xeb, + 0x8f, 0x85, 0x4a, 0x37, 0xb6, 0xe1, 0x88, 0x48, 0x2e, 0x4d, 0x67, 0x27, 0x19, 0x41, 0x05, 0x34, + 0x63, 0x5b, 0xcb, 0x70, 0x52, 0xf1, 0xaa, 0x3c, 0x3b, 0xa5, 0x53, 0xd2, 0xd9, 0x29, 0x1d, 0x4b, + 0x55, 0x5d, 0xc6, 0xb2, 0x31, 0x78, 0x43, 0x56, 0xf5, 0x04, 0x12, 0xaa, 0xea, 0x09, 0xe0, 0x38, + 0x43, 0x8d, 0xb8, 0xc4, 0x53, 0x66, 0xba, 0x31, 0x04, 0x92, 0x38, 0x43, 0x00, 0xc7, 0x19, 0x4e, + 0x11, 0xaf, 0xb1, 0xa2, 0xcc, 0x76, 0x63, 0x08, 0x24, 0x71, 0x86, 0x00, 0xa6, 0x9b, 0x4d, 0x19, + 0x3c, 0xde, 0x69, 0xae, 0xfa, 0x7d, 0x36, 0x27, 0x6f, 0x36, 0x53, 0x09, 0xe9, 0x66, 0x33, 0x15, + 0x89, 0x3f, 0xb1, 0x6d, 0x6f, 0x6e, 0x65, 0x1e, 0x2a, 0xbc, 0x1c, 0xda, 0x05, 0xdb, 0x29, 0x35, + 0xdd, 0xa3, 0x6d, 0xd7, 0x5b, 0xfc, 0xbd, 0x81, 0xd3, 0xa5, 0x52, 0x85, 0xaa, 0x8e, 0x05, 0x67, + 0x15, 0x0c, 0x3c, 0xdd, 0xa3, 0x05, 0x6e, 0x99, 0xcf, 0xa2, 0x41, 0xf8, 0xa8, 0x8a, 0x65, 0x7a, + 0xe5, 0x71, 0xe5, 0x35, 0x79, 0xcb, 0x24, 0xa0, 0xe8, 0x96, 0x49, 0xf8, 0x49, 0x27, 0x71, 0xf8, + 0xc9, 0xa6, 0x98, 0xf2, 0xb8, 0xa2, 0xc9, 0x93, 0xb8, 0x84, 0xa4, 0x93, 0xb8, 0x04, 0x08, 0xea, + 0x2d, 0x3b, 0x76, 0xbb, 0x3c, 0xae, 0xd4, 0x12, 0xea, 0x65, 0xa8, 0xa0, 0x5e, 0xf6, 0x33, 0xa8, + 0xb7, 0xb6, 0xd2, 0xf1, 0xca, 0xf4, 0x1b, 0x17, 0x12, 0xea, 0xf5, 0x91, 0x41, 0xbd, 0x3e, 0x80, + 0x4e, 0x85, 0x00, 0xa8, 0x3a, 0x36, 0x9d, 0xb4, 0x6f, 0x98, 0xcd, 0xa6, 0x72, 0x53, 0x9e, 0x0a, + 0xa3, 0x78, 0x3a, 0x15, 0x46, 0x61, 0xd4, 0xf4, 0x64, 0xad, 0x22, 0x8b, 0x9d, 0x65, 0xe5, 0x96, + 0x6c, 0x7a, 0x86, 0x18, 0x6a, 0x7a, 0x86, 0xbf, 0x60, 0x77, 0x41, 0x7f, 0x69, 0x64, 0xc9, 0x21, + 0xee, 0x8a, 0x72, 0x3b, 0xb2, 0xbb, 0x10, 0x70, 0xb0, 0xbb, 0x10, 0x7e, 0xe3, 0x65, 0x74, 0xbf, + 0xb4, 0xd0, 0xf8, 0x97, 0x36, 0x35, 0xa2, 0x3b, 0x8d, 0x15, 0xe5, 0x03, 0xc0, 0xea, 0xa1, 0xc4, + 0xa5, 0x4a, 0x26, 0x9d, 0xee, 0xd1, 0xba, 0x71, 0x82, 0x6d, 0xf9, 0x6b, 0x33, 0x2c, 0xc2, 0xa4, + 0x56, 0x9d, 0xf0, 0x37, 0xa1, 0xaf, 0x47, 0xb6, 0xe5, 0x71, 0x12, 0xd8, 0x96, 0xc7, 0xc1, 0xb8, + 0x8d, 0xce, 0x47, 0xb6, 0x6a, 0xb3, 0x7a, 0x93, 0xee, 0x4b, 0x88, 0x51, 0xd5, 0x1b, 0xab, 0xc4, + 0x53, 0x3e, 0x08, 0xbc, 0x2f, 0xa4, 0x6c, 0xf8, 0x22, 0xd4, 0xd3, 0x3d, 0xda, 0x16, 0xfc, 0xb0, + 0x8a, 0xf2, 0xb5, 0xa9, 0x85, 0xaa, 0xf2, 0x21, 0xf9, 0x7c, 0x93, 0xc2, 0xa6, 0x7b, 0x34, 0xc0, + 0x51, 0x2b, 0xed, 0x66, 0x7b, 0xd9, 0xd1, 0x0d, 0xc2, 0x0c, 0x2d, 0xb0, 0xdd, 0xb8, 0x01, 0xfa, + 0x61, 0xd9, 0x4a, 0x4b, 0xa3, 0xa3, 0x56, 0x5a, 0x1a, 0x8e, 0x2a, 0xaa, 0x94, 0x4c, 0x41, 0xf9, + 0x88, 0xac, 0xa8, 0x12, 0x92, 0x2a, 0xaa, 0x9c, 0x7a, 0xe1, 0x03, 0xe8, 0x64, 0xb0, 0x9f, 0xe7, + 0xeb, 0x2f, 0xeb, 0x34, 0xe5, 0xa3, 0xc0, 0xe7, 0x7c, 0xec, 0x32, 0x40, 0xa2, 0x9a, 0xee, 0xd1, + 0x52, 0xca, 0xd3, 0x15, 0x37, 0x96, 0x27, 0x88, 0x9b, 0x17, 0xdf, 0x22, 0xaf, 0xb8, 0x29, 0x64, + 0x74, 0xc5, 0x4d, 0x41, 0x25, 0x32, 0xe7, 0x42, 0xd5, 0xb7, 0x60, 0x1e, 0xc8, 0x34, 0x8d, 0x43, + 0x22, 0x73, 0x6e, 0xa9, 0x2d, 0x6e, 0xc1, 0x3c, 0xb0, 0xd6, 0xd2, 0x38, 0xe0, 0x8b, 0xa8, 0x50, + 0xab, 0xcd, 0x6a, 0x1d, 0x4b, 0x69, 0x44, 0xbc, 0x65, 0x01, 0x3a, 0xdd, 0xa3, 0x71, 0x3c, 0x35, + 0x83, 0x26, 0x9b, 0xba, 0xeb, 0x99, 0x0d, 0x17, 0x46, 0x8c, 0x3f, 0x42, 0x0c, 0xd9, 0x0c, 0x4a, + 0xa2, 0xa1, 0x66, 0x50, 0x12, 0x9c, 0xda, 0x8b, 0x13, 0xba, 0xeb, 0xea, 0x96, 0xe1, 0xe8, 0xe3, + 0xb0, 0x4c, 0x90, 0xc8, 0x9b, 0x22, 0x09, 0x4b, 0xed, 0x45, 0x19, 0x02, 0x87, 0xef, 0x3e, 0xc4, + 0x37, 0x73, 0x96, 0x22, 0x87, 0xef, 0x11, 0x3c, 0x1c, 0xbe, 0x47, 0x60, 0x60, 0x77, 0xfa, 0x30, + 0x8d, 0x2c, 0x9b, 0x54, 0x44, 0xca, 0x72, 0xc4, 0xee, 0x8c, 0x12, 0x80, 0xdd, 0x19, 0x05, 0x4a, + 0x4d, 0xf2, 0x97, 0xdb, 0x95, 0x94, 0x26, 0x85, 0xab, 0x6c, 0xac, 0x0c, 0x5d, 0xbf, 0xc3, 0xc1, + 0x51, 0x5e, 0xb7, 0xf4, 0x96, 0x5d, 0x1e, 0xf7, 0xa5, 0x6e, 0xca, 0xeb, 0x77, 0x2a, 0x21, 0x5d, + 0xbf, 0x53, 0x91, 0x74, 0x76, 0xf5, 0x37, 0x5a, 0x2b, 0xba, 0x43, 0x8c, 0xb2, 0xe9, 0xc0, 0xc9, + 0xe2, 0x3a, 0xdb, 0x1a, 0xbe, 0x21, 0xcf, 0xae, 0x5d, 0x48, 0xe9, 0xec, 0xda, 0x05, 0x4d, 0x8d, + 0xbc, 0x64, 0xb4, 0x46, 0x74, 0x43, 0x59, 0x95, 0x8d, 0xbc, 0x74, 0x4a, 0x6a, 0xe4, 0xa5, 0x63, + 0xd3, 0x3f, 0xe7, 0xb6, 0x63, 0x7a, 0x44, 0x69, 0x6e, 0xe7, 0x73, 0x80, 0x34, 0xfd, 0x73, 0x00, + 0x4d, 0x37, 0x84, 0xd1, 0x0e, 0x69, 0xc9, 0x1b, 0xc2, 0x78, 0x37, 0x44, 0x4b, 0x50, 0x8b, 0x85, + 0x3f, 0x2d, 0x53, 0x2c, 0xd9, 0x62, 0xe1, 0x60, 0x6a, 0xb1, 0x84, 0x8f, 0xcf, 0xa4, 0xa7, 0x4c, + 0x8a, 0x2d, 0xaf, 0xa1, 0x22, 0x8e, 0xae, 0xa1, 0xd2, 0xb3, 0xa7, 0x67, 0xa5, 0x77, 0x06, 0x4a, + 0x5b, 0xb6, 0x3a, 0x04, 0x14, 0xb5, 0x3a, 0xc4, 0x17, 0x09, 0x13, 0xe8, 0x18, 0xdc, 0x82, 0x6b, + 0x9d, 0xe0, 0x1e, 0xe7, 0x63, 0xf2, 0x67, 0x46, 0xd0, 0xf4, 0x33, 0x23, 0x20, 0x89, 0x09, 0x9f, + 0xb6, 0x9c, 0x14, 0x26, 0xe1, 0xf9, 0x60, 0x04, 0x84, 0x67, 0x10, 0xae, 0x95, 0x66, 0x67, 0x2a, + 0x46, 0x55, 0xbc, 0x22, 0x73, 0xe5, 0x13, 0xd8, 0x38, 0xc5, 0x74, 0x8f, 0x96, 0x50, 0x0e, 0xbf, + 0x81, 0xce, 0x72, 0x28, 0x7f, 0x37, 0x0c, 0x49, 0xbc, 0x8d, 0x60, 0x41, 0xf0, 0x64, 0x3f, 0xb6, + 0x6e, 0xb4, 0xd3, 0x3d, 0x5a, 0x57, 0x5e, 0xe9, 0x75, 0xf1, 0xf5, 0xa1, 0xb3, 0x9d, 0xba, 0x82, + 0x45, 0xa2, 0x2b, 0xaf, 0xf4, 0xba, 0xb8, 0xdc, 0xef, 0x6c, 0xa7, 0xae, 0xa0, 0x13, 0xba, 0xf2, + 0xc2, 0x2e, 0x2a, 0x76, 0xc3, 0x97, 0x9a, 0x4d, 0x65, 0x0d, 0xaa, 0x7b, 0xcf, 0x76, 0xaa, 0x2b, + 0x81, 0xc1, 0xb9, 0x15, 0x47, 0x3a, 0x4b, 0xcf, 0xb7, 0x89, 0x55, 0x93, 0x16, 0xa0, 0xbb, 0xf2, + 0x2c, 0x1d, 0x23, 0xa0, 0xb3, 0x74, 0x0c, 0x48, 0x07, 0x94, 0xf8, 0x5c, 0x45, 0x59, 0x97, 0x07, + 0x94, 0x88, 0xa3, 0x03, 0x4a, 0x7a, 0xda, 0x32, 0x8f, 0x8e, 0xcf, 0xaf, 0x7a, 0xba, 0x6f, 0x41, + 0xba, 0xbc, 0x2b, 0xdf, 0x8c, 0x5c, 0x32, 0xc5, 0x49, 0xe0, 0x92, 0x29, 0x0e, 0xa6, 0x63, 0x84, + 0x82, 0x6b, 0xeb, 0x56, 0x63, 0x4a, 0x37, 0x9b, 0x1d, 0x87, 0x28, 0xff, 0x87, 0x3c, 0x46, 0x22, + 0x68, 0x3a, 0x46, 0x22, 0x20, 0xba, 0x40, 0x53, 0x50, 0xc9, 0x75, 0xcd, 0x65, 0x8b, 0xef, 0x2b, + 0x3b, 0x4d, 0x4f, 0xf9, 0x3f, 0xe5, 0x05, 0x3a, 0x89, 0x86, 0x2e, 0xd0, 0x49, 0x70, 0x38, 0x75, + 0x4a, 0x48, 0x70, 0xaf, 0xfc, 0x5f, 0x91, 0x53, 0xa7, 0x04, 0x1a, 0x38, 0x75, 0x4a, 0x4a, 0x8e, + 0x3f, 0x85, 0x46, 0x99, 0x4d, 0x36, 0x63, 0x06, 0x77, 0xd5, 0xff, 0xb7, 0xbc, 0x3e, 0x46, 0xf1, + 0x74, 0x7d, 0x8c, 0xc2, 0x64, 0x3e, 0xbc, 0x0b, 0xfe, 0x9f, 0x34, 0x3e, 0x81, 0xfc, 0x63, 0x65, + 0xf0, 0x75, 0x91, 0x0f, 0x1f, 0x29, 0xdf, 0x9a, 0x49, 0x63, 0x14, 0x0c, 0x8f, 0x58, 0x21, 0x99, + 0x91, 0x46, 0xee, 0x98, 0x64, 0x4d, 0xf9, 0x78, 0x2a, 0x23, 0x46, 0x20, 0x33, 0x62, 0x30, 0xfc, + 0x3a, 0x3a, 0x19, 0xc2, 0x66, 0x49, 0x6b, 0x31, 0x98, 0x99, 0xbe, 0x2d, 0x23, 0x9b, 0xc1, 0xc9, + 0x64, 0xd4, 0x0c, 0x4e, 0xc6, 0x24, 0xb1, 0xe6, 0xa2, 0xfb, 0x7f, 0xb7, 0x60, 0x1d, 0x48, 0x30, + 0x85, 0x41, 0x12, 0x6b, 0x2e, 0xcd, 0x6f, 0xdf, 0x82, 0x75, 0x20, 0xd3, 0x14, 0x06, 0xf8, 0x93, + 0x19, 0x74, 0x21, 0x19, 0x55, 0x6a, 0x36, 0xa7, 0x6c, 0x27, 0xc4, 0x29, 0xdf, 0x91, 0x91, 0x0f, + 0x1a, 0xb6, 0x57, 0x6c, 0xba, 0x47, 0xdb, 0x66, 0x05, 0xf8, 0xfd, 0x68, 0xb8, 0xd4, 0x31, 0x4c, + 0x0f, 0x2e, 0xde, 0xa8, 0xe1, 0xfc, 0x9d, 0x99, 0xc8, 0x16, 0x47, 0xc4, 0xc2, 0x16, 0x47, 0x04, + 0xe0, 0x57, 0xd1, 0x58, 0x8d, 0x34, 0x3a, 0x8e, 0xe9, 0xad, 0x6b, 0xa4, 0x6d, 0x3b, 0x1e, 0xe5, + 0xf1, 0x5d, 0x19, 0x79, 0x12, 0x8b, 0x51, 0xd0, 0x49, 0x2c, 0x06, 0xc4, 0xb7, 0x52, 0x52, 0xdc, + 0x2b, 0x9f, 0xc8, 0x74, 0xbd, 0x96, 0x0f, 0xfa, 0x32, 0x25, 0x43, 0x7e, 0x35, 0x31, 0x65, 0xb8, + 0xf2, 0xc9, 0x4c, 0x97, 0x6b, 0x74, 0x61, 0x86, 0x4b, 0xc8, 0x36, 0x5e, 0x4d, 0x4c, 0x0a, 0xad, + 0x7c, 0x77, 0xa6, 0xcb, 0xb5, 0x77, 0xc8, 0x31, 0x29, 0x9f, 0xf4, 0xd3, 0xcc, 0x53, 0x84, 0x33, + 0xfa, 0x9e, 0x4c, 0xdc, 0x55, 0x24, 0x28, 0x2f, 0x10, 0xd2, 0x62, 0x37, 0xdd, 0x40, 0xe9, 0x3f, + 0x95, 0x89, 0xfb, 0xe6, 0x85, 0xc5, 0xc2, 0x5f, 0x98, 0xa0, 0x33, 0x93, 0x77, 0x3d, 0xe2, 0x58, + 0x7a, 0x13, 0xba, 0xb3, 0xe6, 0xd9, 0x8e, 0xbe, 0x4c, 0x26, 0x2d, 0x7d, 0xb1, 0x49, 0x94, 0x4f, + 0x67, 0x64, 0x0b, 0x36, 0x9d, 0x94, 0x5a, 0xb0, 0xe9, 0x58, 0xbc, 0x82, 0xee, 0x4f, 0xc2, 0x96, + 0x4d, 0x17, 0xea, 0xf9, 0x4c, 0x46, 0x36, 0x61, 0xbb, 0xd0, 0x52, 0x13, 0xb6, 0x0b, 0x1a, 0x5f, + 0x45, 0x03, 0xe3, 0xb6, 0x3f, 0xfd, 0x7e, 0x6f, 0xc4, 0x19, 0x32, 0xc0, 0x4c, 0xf7, 0x68, 0x21, + 0x19, 0x2f, 0xc3, 0x07, 0xf5, 0x67, 0xe3, 0x65, 0xc2, 0xcb, 0xa7, 0xe0, 0x07, 0x2f, 0xc3, 0xc5, + 0xfd, 0xff, 0xc5, 0xcb, 0x84, 0x77, 0x5c, 0xc1, 0x0f, 0x3a, 0x93, 0xb0, 0x1a, 0x67, 0xa7, 0x4a, + 0xd4, 0x6e, 0x9b, 0x58, 0xd1, 0x9b, 0x4d, 0x62, 0x2d, 0x13, 0xe5, 0x73, 0x91, 0x99, 0x24, 0x99, + 0x8c, 0xce, 0x24, 0xc9, 0x18, 0xfc, 0x61, 0x74, 0xea, 0x96, 0xde, 0x34, 0x8d, 0x10, 0xe7, 0xe7, + 0x19, 0x56, 0xbe, 0x2f, 0x23, 0xef, 0xa6, 0x53, 0xe8, 0xe8, 0x6e, 0x3a, 0x05, 0x85, 0x67, 0x11, + 0x86, 0x65, 0x34, 0x98, 0x2d, 0xe8, 0xfa, 0xac, 0xfc, 0xff, 0x19, 0xd9, 0x4e, 0x8d, 0x93, 0x50, + 0x3b, 0x35, 0x0e, 0xc5, 0xf5, 0xf4, 0x64, 0x10, 0xca, 0xf7, 0x67, 0xe4, 0xd3, 0x9a, 0x34, 0xc2, + 0xe9, 0x1e, 0x2d, 0x3d, 0xa3, 0xc4, 0x75, 0x34, 0x5a, 0xab, 0x56, 0xa6, 0xa6, 0x26, 0x6b, 0xb7, + 0x2a, 0x65, 0x78, 0xe8, 0x60, 0x28, 0x3f, 0x10, 0x59, 0xb1, 0xa2, 0x04, 0x74, 0xc5, 0x8a, 0xc2, + 0x70, 0x0d, 0x9d, 0xa0, 0x82, 0xa8, 0x3a, 0x64, 0x89, 0x38, 0xc4, 0x6a, 0xf8, 0xc3, 0xf2, 0x87, + 0x32, 0xb2, 0xa1, 0x90, 0x44, 0x44, 0x0d, 0x85, 0x24, 0x38, 0x5e, 0x45, 0x67, 0xa3, 0x87, 0x39, + 0xe2, 0xcb, 0x51, 0xe5, 0x87, 0x33, 0x11, 0x7b, 0xb6, 0x0b, 0x31, 0xd8, 0xb3, 0x5d, 0xf0, 0xd8, + 0x42, 0xe7, 0xf8, 0xc9, 0x08, 0xf7, 0x99, 0x8c, 0xd6, 0xf6, 0x23, 0xac, 0xb6, 0x47, 0x42, 0x9f, + 0xbe, 0x2e, 0xd4, 0xd3, 0x3d, 0x5a, 0x77, 0x76, 0x54, 0x55, 0xe2, 0x59, 0x0b, 0x94, 0x1f, 0xcd, + 0x24, 0x3b, 0x95, 0x48, 0x9e, 0xc6, 0x49, 0xe9, 0x0e, 0x5e, 0x4f, 0x8b, 0xb9, 0xaf, 0xfc, 0x58, + 0x64, 0xc8, 0x24, 0x93, 0xd1, 0x21, 0x93, 0x12, 0xb4, 0xff, 0x55, 0x34, 0xc6, 0xf4, 0xb2, 0xaa, + 0xc3, 0x48, 0xb2, 0x96, 0x89, 0xa1, 0xfc, 0x8d, 0xc8, 0x82, 0x15, 0xa3, 0x00, 0xef, 0x9c, 0x28, + 0x90, 0xce, 0xbe, 0xb5, 0xb6, 0x6e, 0x59, 0x70, 0x52, 0xaa, 0xfc, 0xcd, 0xc8, 0xec, 0x1b, 0xa2, + 0xc0, 0xf7, 0x36, 0xf8, 0x05, 0xb9, 0xd3, 0x92, 0x1e, 0x56, 0x2b, 0x9f, 0x8f, 0xac, 0x73, 0x89, + 0x54, 0x74, 0x9d, 0x4b, 0x7e, 0x97, 0x7d, 0x2b, 0xe5, 0x51, 0xb2, 0xf2, 0xe3, 0xdd, 0xf9, 0x86, + 0xeb, 0x67, 0xf2, 0x9b, 0xe6, 0x5b, 0x29, 0x0f, 0x7a, 0x95, 0x9f, 0xe8, 0xce, 0x37, 0x74, 0x97, + 0x4b, 0x7e, 0x0f, 0x5c, 0x4f, 0x7f, 0x0c, 0xab, 0xfc, 0x64, 0x74, 0x42, 0x48, 0x21, 0x84, 0x09, + 0x21, 0xed, 0x45, 0xed, 0x22, 0x3a, 0xcd, 0x3a, 0xed, 0xba, 0xa3, 0xb7, 0x57, 0x6a, 0xc4, 0xf3, + 0x4c, 0x6b, 0xd9, 0xdf, 0xdf, 0xfc, 0xed, 0x4c, 0xe4, 0xd0, 0x29, 0x8d, 0x12, 0x0e, 0x9d, 0xd2, + 0x90, 0x54, 0x9f, 0x62, 0xcf, 0x5e, 0x95, 0x9f, 0x8a, 0xe8, 0x53, 0x8c, 0x82, 0xea, 0x53, 0xfc, + 0xb5, 0xec, 0xab, 0x09, 0xaf, 0x3b, 0x95, 0xbf, 0x93, 0xce, 0x2b, 0x68, 0x5f, 0xc2, 0xa3, 0xd0, + 0x57, 0x13, 0x1e, 0x31, 0x2a, 0x7f, 0x37, 0x9d, 0x57, 0xe8, 0xd9, 0x13, 0x7f, 0xfb, 0xf8, 0x3a, + 0x3a, 0xc9, 0xe6, 0xc8, 0x29, 0x62, 0x10, 0xe9, 0x43, 0x7f, 0x3a, 0x32, 0x1c, 0x93, 0xc9, 0xe0, + 0x20, 0x3b, 0x11, 0x93, 0xc4, 0x9a, 0xb7, 0xf5, 0x67, 0xb6, 0x60, 0x1d, 0x9a, 0xd9, 0xc9, 0x18, + 0xfc, 0x82, 0xfc, 0xa4, 0x4c, 0xf9, 0xd9, 0x4c, 0xc4, 0x49, 0x42, 0x40, 0x82, 0x93, 0x84, 0xf8, + 0xfe, 0xec, 0x05, 0xf9, 0xf9, 0x94, 0xf2, 0xf7, 0x12, 0x0b, 0x07, 0x1d, 0x20, 0xbf, 0xb5, 0x7a, + 0x41, 0x7e, 0x2a, 0xa4, 0xfc, 0x5c, 0x62, 0xe1, 0xe0, 0x03, 0xe4, 0x77, 0x45, 0x74, 0xe3, 0xd1, + 0xf1, 0x6c, 0xc6, 0x4a, 0x9a, 0x1e, 0x7e, 0x3e, 0xba, 0xf1, 0x48, 0x24, 0x83, 0x8d, 0x47, 0x22, + 0x26, 0x89, 0x35, 0xff, 0xbc, 0x5f, 0xd8, 0x82, 0xb5, 0xb0, 0x5d, 0x4a, 0xc4, 0x24, 0xb1, 0xe6, + 0x1f, 0xff, 0x85, 0x2d, 0x58, 0x0b, 0xdb, 0xa5, 0x44, 0x0c, 0x35, 0x72, 0x42, 0xcc, 0x2d, 0xe2, + 0xb8, 0xa1, 0xfa, 0xfd, 0x62, 0xc4, 0xc8, 0x49, 0xa1, 0xa3, 0x46, 0x4e, 0x0a, 0x2a, 0x91, 0x3b, + 0x17, 0xca, 0x2f, 0x6d, 0xc5, 0x3d, 0xbc, 0xed, 0x48, 0x41, 0x25, 0x72, 0xe7, 0x72, 0xf9, 0xe5, + 0xad, 0xb8, 0x87, 0xd7, 0x1d, 0x29, 0x28, 0x6a, 0xf0, 0x4c, 0x38, 0xf6, 0x9a, 0xf5, 0x2a, 0x59, + 0x23, 0x4d, 0x2e, 0x92, 0x2f, 0x46, 0x0c, 0x9e, 0x28, 0x01, 0x1c, 0xce, 0x47, 0x60, 0x32, 0x23, + 0xfe, 0xf5, 0xbf, 0x9a, 0xca, 0x28, 0x3c, 0x7d, 0x88, 0xc2, 0x64, 0x46, 0xfc, 0x43, 0x7f, 0x2d, + 0x95, 0x51, 0x78, 0xfa, 0x10, 0x85, 0x8d, 0xf7, 0xa1, 0x5e, 0x38, 0x9e, 0x52, 0x3f, 0x9f, 0x41, + 0x43, 0x35, 0xcf, 0x21, 0x7a, 0x8b, 0x07, 0xa1, 0x3a, 0x83, 0xfa, 0x99, 0x9f, 0xa7, 0xff, 0x28, + 0x55, 0x0b, 0x7e, 0xe3, 0x0b, 0x68, 0x64, 0x46, 0x77, 0x3d, 0x28, 0x59, 0xb1, 0x0c, 0x72, 0x17, + 0xde, 0x38, 0xe5, 0xb4, 0x08, 0x14, 0xcf, 0x30, 0x3a, 0x56, 0x0e, 0xa2, 0xff, 0xe5, 0xb6, 0x8c, + 0xbd, 0xd4, 0xff, 0xd6, 0x46, 0xb1, 0x07, 0x42, 0x2d, 0x45, 0xca, 0xaa, 0x5f, 0xcd, 0xa0, 0x98, + 0x07, 0xea, 0xee, 0x1f, 0x8b, 0xcf, 0xa3, 0x63, 0x91, 0x88, 0x93, 0xfc, 0xa1, 0xd6, 0x36, 0x03, + 0x52, 0x46, 0x4b, 0xe3, 0xf7, 0x04, 0x0f, 0x84, 0x6e, 0x6a, 0x33, 0x3c, 0xae, 0x56, 0xdf, 0xe6, + 0x46, 0x31, 0xd7, 0x71, 0x9a, 0x9a, 0x80, 0xe2, 0x71, 0x5f, 0xfe, 0xfe, 0x68, 0x18, 0x4e, 0x0f, + 0x5f, 0xe0, 0x2f, 0xd7, 0x33, 0x61, 0x34, 0xae, 0x48, 0x5e, 0x5e, 0xf6, 0x52, 0xfd, 0xfd, 0x68, + 0xa8, 0xd2, 0x6a, 0x13, 0xc7, 0xb5, 0x2d, 0xdd, 0xb3, 0x1d, 0xfe, 0xf0, 0x17, 0x22, 0x35, 0x99, + 0x02, 0x5c, 0x8c, 0x1e, 0x24, 0xd2, 0xe3, 0x4b, 0x7e, 0x1a, 0xa6, 0x1c, 0x04, 0x32, 0x84, 0xd7, + 0x7b, 0xd1, 0x5c, 0xaf, 0x8c, 0x82, 0x92, 0xde, 0x74, 0x75, 0x78, 0x4a, 0x16, 0x90, 0x76, 0x28, + 0x40, 0x24, 0x05, 0x0a, 0xfc, 0x18, 0x2a, 0xc0, 0xd1, 0xbb, 0x0b, 0xe9, 0xd5, 0x78, 0x8c, 0xb0, + 0x26, 0x40, 0xc4, 0x88, 0x4c, 0x8c, 0x06, 0xdf, 0x40, 0xa3, 0xe1, 0xbd, 0xe2, 0x75, 0xc7, 0xee, + 0xb4, 0xfd, 0x84, 0x0a, 0x90, 0x23, 0x77, 0x35, 0xc0, 0xd5, 0x97, 0x01, 0x29, 0xb0, 0x88, 0x15, + 0xc4, 0xd3, 0xe8, 0x58, 0x08, 0xa3, 0x22, 0xf2, 0x13, 0xb9, 0x40, 0x8e, 0x68, 0x81, 0x17, 0x15, + 0xa7, 0xc8, 0x2a, 0x5a, 0x0c, 0x57, 0x50, 0x9f, 0x1f, 0x20, 0xac, 0x7f, 0x4b, 0x25, 0x3d, 0xce, + 0x03, 0x84, 0xf5, 0x89, 0xa1, 0xc1, 0xfc, 0xf2, 0x78, 0x0a, 0x8d, 0x68, 0x76, 0xc7, 0x23, 0x0b, + 0x36, 0xb7, 0xe6, 0x79, 0xa4, 0x7f, 0x68, 0x93, 0x43, 0x31, 0x75, 0xcf, 0xf6, 0x53, 0x0c, 0x8b, + 0xa9, 0x6e, 0xe5, 0x52, 0x78, 0x0e, 0x8d, 0xc5, 0x6e, 0x60, 0xc5, 0xc4, 0xbf, 0xc2, 0xe7, 0xc5, + 0x99, 0xc5, 0x8b, 0xe2, 0xef, 0xcc, 0xa0, 0xc2, 0x82, 0xa3, 0x9b, 0x9e, 0xcb, 0x5f, 0xa1, 0xdd, + 0x77, 0x79, 0xcd, 0xd1, 0xdb, 0x54, 0x3f, 0x2e, 0x43, 0xa4, 0xca, 0x5b, 0x7a, 0xb3, 0x43, 0xdc, + 0xf1, 0xdb, 0xf4, 0xeb, 0xfe, 0xf5, 0x46, 0xf1, 0x85, 0x65, 0x38, 0xe7, 0xb9, 0xdc, 0xb0, 0x5b, + 0x57, 0x96, 0x1d, 0xfd, 0x8e, 0xe9, 0xc1, 0xba, 0xaf, 0x37, 0xaf, 0x78, 0xa4, 0x09, 0xc7, 0x49, + 0x57, 0xf4, 0xb6, 0x79, 0x05, 0x22, 0x22, 0x5f, 0x09, 0x38, 0xb1, 0x1a, 0xa8, 0x0a, 0x78, 0xf0, + 0x97, 0xa8, 0x02, 0x0c, 0x87, 0xe7, 0x10, 0xe2, 0x9f, 0x5a, 0x6a, 0xb7, 0xf9, 0x93, 0x36, 0xe1, + 0x10, 0xc6, 0xc7, 0x30, 0xc5, 0x0e, 0x04, 0xa6, 0xb7, 0xdb, 0x62, 0x6a, 0xf1, 0x90, 0x8e, 0x6a, + 0xc1, 0x02, 0x6f, 0x91, 0x2f, 0xa6, 0xe1, 0x50, 0xe2, 0x7e, 0x63, 0x13, 0x84, 0x14, 0x2d, 0x86, + 0x17, 0xd1, 0x31, 0xce, 0x37, 0x08, 0xbd, 0x3f, 0x22, 0xcf, 0x0a, 0x11, 0x34, 0x53, 0xda, 0xa0, + 0x8d, 0x06, 0x07, 0x8b, 0x75, 0x44, 0x4a, 0xe0, 0xf1, 0x30, 0xeb, 0xe4, 0x9c, 0xde, 0x22, 0xae, + 0x72, 0x0c, 0x34, 0xf6, 0xec, 0xe6, 0x46, 0x51, 0xf1, 0xcb, 0x43, 0xac, 0xbc, 0xc4, 0x0c, 0xcc, + 0x50, 0x44, 0xe4, 0xc1, 0xb4, 0x7e, 0x34, 0x81, 0x47, 0x54, 0xe7, 0xe5, 0x22, 0x78, 0x02, 0x0d, + 0x07, 0x1e, 0xf5, 0x37, 0x6f, 0x56, 0xca, 0xf0, 0x66, 0x6e, 0x60, 0xfc, 0xdc, 0xe6, 0x46, 0xf1, + 0x74, 0x24, 0xaa, 0xbf, 0xc8, 0x44, 0x2a, 0x23, 0x84, 0x21, 0x60, 0x8f, 0xe8, 0x22, 0x61, 0x08, + 0xda, 0x09, 0x61, 0x08, 0xaa, 0xf8, 0x25, 0x34, 0x58, 0xba, 0x5d, 0xe3, 0xe1, 0x15, 0x5c, 0xe5, + 0x78, 0x98, 0x4e, 0x05, 0x92, 0x70, 0xf3, 0x50, 0x0c, 0x62, 0xd3, 0x45, 0x7a, 0x3c, 0x89, 0x46, + 0x24, 0xa7, 0x1c, 0x57, 0x39, 0x01, 0x1c, 0xa0, 0xe5, 0x3a, 0x60, 0xea, 0x0e, 0x47, 0x49, 0x69, + 0xe1, 0xa5, 0x42, 0x54, 0x6b, 0xca, 0xa6, 0x0b, 0xa9, 0x29, 0x34, 0x02, 0x91, 0x1c, 0xe0, 0x05, + 0x5e, 0x3f, 0xd3, 0x1a, 0x83, 0xa3, 0xea, 0x0e, 0xc3, 0x49, 0x79, 0xe0, 0xe5, 0x62, 0xf8, 0x0d, + 0x84, 0x21, 0x99, 0x05, 0x31, 0xfc, 0x3b, 0x9a, 0x4a, 0xd9, 0x55, 0x4e, 0x42, 0xc4, 0x5e, 0x1c, + 0x7d, 0x39, 0x5e, 0x29, 0x8f, 0x5f, 0xe0, 0xd3, 0xc7, 0x79, 0x9d, 0x95, 0xaa, 0xfb, 0xaf, 0xc6, + 0xeb, 0xa6, 0x21, 0xb6, 0x38, 0x81, 0x2b, 0x5e, 0x43, 0xa7, 0xaa, 0x0e, 0xb9, 0x63, 0xda, 0x1d, + 0xd7, 0x5f, 0x3e, 0xfc, 0x79, 0xeb, 0xd4, 0x96, 0xf3, 0xd6, 0x83, 0xbc, 0xe2, 0xfb, 0xda, 0x0e, + 0xb9, 0x53, 0xf7, 0xe3, 0xb4, 0x4a, 0x01, 0x0e, 0xd3, 0xb8, 0x53, 0x71, 0x41, 0x14, 0x0b, 0x0e, + 0x37, 0x89, 0xab, 0x28, 0xe1, 0x54, 0xcb, 0x82, 0x72, 0x98, 0x01, 0x4e, 0x14, 0x57, 0xa4, 0x18, + 0xd6, 0x10, 0xbe, 0x3e, 0xe1, 0xdf, 0xd7, 0x95, 0x1a, 0x2c, 0xab, 0xa3, 0x72, 0x1a, 0x98, 0xa9, + 0x54, 0x2c, 0xcb, 0x8d, 0x20, 0x66, 0x73, 0x5d, 0xe7, 0x78, 0x51, 0x2c, 0xf1, 0xd2, 0x78, 0x06, + 0x8d, 0x56, 0x1d, 0x38, 0x7a, 0xb8, 0x41, 0xd6, 0xab, 0x76, 0xd3, 0x6c, 0xac, 0xc3, 0x43, 0x40, + 0x3e, 0x55, 0xb6, 0x19, 0xae, 0xbe, 0x4a, 0xd6, 0xeb, 0x6d, 0xc0, 0x8a, 0xcb, 0x4a, 0xb4, 0xa4, + 0x18, 0x43, 0xf5, 0xfe, 0xed, 0xc5, 0x50, 0x25, 0x68, 0x94, 0xdf, 0xf6, 0xdd, 0xf5, 0x88, 0x45, + 0x97, 0x7a, 0x97, 0x3f, 0xfa, 0x53, 0x22, 0xb7, 0x83, 0x01, 0x9e, 0xe7, 0x84, 0x67, 0xa3, 0x8c, + 0x04, 0x60, 0xb1, 0x61, 0xd1, 0x22, 0xea, 0x67, 0x72, 0xe2, 0xd4, 0x89, 0xcf, 0xa2, 0xbc, 0x90, + 0xc2, 0x03, 0x42, 0x2f, 0x42, 0xb8, 0xe3, 0x3c, 0x8f, 0xeb, 0x3a, 0xc0, 0xcd, 0x8e, 0x20, 0x46, + 0x08, 0xe4, 0x37, 0xf3, 0xe3, 0x2a, 0x9b, 0x86, 0x16, 0x12, 0x40, 0x6e, 0xa9, 0xce, 0x62, 0xd3, + 0x6c, 0x40, 0x10, 0xec, 0x9c, 0x10, 0x14, 0x00, 0xa0, 0x2c, 0x06, 0xb6, 0x40, 0x82, 0xaf, 0xa2, + 0x41, 0xff, 0xb4, 0x2a, 0x0c, 0x3d, 0x0a, 0xb1, 0x91, 0xfd, 0xec, 0xf9, 0x2c, 0xf4, 0xb2, 0x40, + 0x84, 0x9f, 0x47, 0x28, 0x1c, 0xc9, 0xdc, 0x48, 0x82, 0x59, 0x5e, 0x1c, 0xf8, 0xe2, 0x2c, 0x1f, + 0x52, 0xd3, 0x39, 0x4f, 0xd4, 0x24, 0x3f, 0x9b, 0x1e, 0xcc, 0x79, 0x92, 0xfa, 0x89, 0x7d, 0x2b, + 0x17, 0xc1, 0xf3, 0x68, 0x2c, 0xa6, 0x3c, 0x3c, 0x50, 0x29, 0xe4, 0xed, 0x4d, 0xd0, 0x3c, 0x71, + 0x4d, 0x8d, 0x95, 0x55, 0xbf, 0x2d, 0x1b, 0x5b, 0x31, 0xa8, 0x60, 0x38, 0x95, 0xd0, 0x39, 0x20, + 0x18, 0x9f, 0x35, 0x13, 0x8c, 0x40, 0x84, 0x2f, 0xa2, 0xfe, 0x2a, 0x1d, 0xaf, 0x0d, 0xbb, 0xc9, + 0xbb, 0x0a, 0x22, 0xf2, 0xb4, 0x39, 0x4c, 0x0b, 0xb0, 0xf8, 0xaa, 0x90, 0xdf, 0x51, 0x08, 0xf0, + 0xeb, 0xe7, 0x77, 0x14, 0x27, 0xdc, 0x20, 0xd3, 0xe3, 0xd5, 0x48, 0x2a, 0x19, 0x5e, 0x26, 0x61, + 0xb5, 0x0a, 0x13, 0x65, 0x05, 0xb6, 0x62, 0xef, 0x56, 0xb6, 0xa2, 0xfa, 0x9b, 0x99, 0xb8, 0xf6, + 0xe3, 0x6b, 0xf1, 0x28, 0x9f, 0xb0, 0x34, 0x04, 0x40, 0xb1, 0xd6, 0x20, 0xde, 0xa7, 0x14, 0xaf, + 0x33, 0xbb, 0xeb, 0x78, 0x9d, 0xb9, 0x1d, 0xc6, 0xeb, 0x54, 0xff, 0x47, 0xbe, 0xab, 0xcf, 0xe8, + 0x81, 0xc4, 0xa5, 0x7a, 0x8e, 0xee, 0x77, 0x68, 0xed, 0x25, 0x37, 0x66, 0xb5, 0x33, 0x97, 0xb8, + 0xba, 0xce, 0x46, 0x8d, 0xab, 0xc9, 0x94, 0xf8, 0x65, 0x34, 0xe4, 0x7f, 0x00, 0xc4, 0x81, 0x15, + 0xe2, 0x97, 0x06, 0x6b, 0x4d, 0x24, 0x62, 0xaa, 0x54, 0x00, 0x3f, 0x8d, 0x06, 0xc0, 0xd2, 0x68, + 0xeb, 0x0d, 0x3f, 0x48, 0x30, 0x8b, 0x2a, 0xec, 0x03, 0xc5, 0xd8, 0x4b, 0x01, 0x25, 0xfe, 0x08, + 0x2a, 0xf0, 0x78, 0xf5, 0x2c, 0xf5, 0xf1, 0x95, 0x6d, 0x38, 0xd9, 0x5e, 0x16, 0x63, 0xd5, 0xb3, + 0xbd, 0x03, 0x00, 0xa4, 0xbd, 0x03, 0x0b, 0x53, 0xbf, 0x80, 0x8e, 0x57, 0x1d, 0x62, 0x80, 0x3b, + 0xf7, 0xe4, 0xdd, 0xb6, 0xc3, 0x33, 0x09, 0xb0, 0x01, 0x0c, 0x4b, 0x47, 0xdb, 0x47, 0xd3, 0x45, + 0x8d, 0xe3, 0x05, 0x46, 0x49, 0xc5, 0xa9, 0x3d, 0xc1, 0x5a, 0x72, 0x83, 0xac, 0xaf, 0xd9, 0x8e, + 0xc1, 0x82, 0xed, 0x73, 0x7b, 0x82, 0x0b, 0x7a, 0x95, 0xa3, 0x44, 0x7b, 0x42, 0x2e, 0x74, 0xe6, + 0x39, 0x34, 0xb8, 0xdb, 0x78, 0xef, 0xbf, 0x94, 0x4d, 0x79, 0x7d, 0x71, 0xef, 0xe6, 0xe9, 0x0b, + 0x72, 0xa6, 0xf6, 0xa6, 0xe4, 0x4c, 0xfd, 0x46, 0x36, 0xe5, 0x69, 0xc9, 0x3d, 0x9d, 0xdb, 0x30, + 0x10, 0x86, 0x9c, 0xdb, 0x30, 0x4c, 0x2b, 0x69, 0x1a, 0x9a, 0x48, 0x14, 0xc9, 0x82, 0x5a, 0xd8, + 0x32, 0x0b, 0xea, 0x4f, 0xe7, 0xba, 0x3d, 0xbd, 0x39, 0x92, 0xfd, 0x4e, 0x64, 0x7f, 0x15, 0x0d, + 0x06, 0x92, 0xad, 0x94, 0xc1, 0x9e, 0x19, 0x0e, 0xb2, 0x4b, 0x30, 0x30, 0x94, 0x11, 0x88, 0xf0, + 0x25, 0xd6, 0xd6, 0x9a, 0xf9, 0x26, 0x8b, 0xb0, 0x3e, 0xcc, 0x63, 0x67, 0xeb, 0x9e, 0x5e, 0x77, + 0xcd, 0x37, 0x89, 0x16, 0xa0, 0xd5, 0x7f, 0x94, 0x4d, 0x7c, 0xbf, 0x74, 0xd4, 0x47, 0x3b, 0xe8, + 0xa3, 0x04, 0x21, 0xb2, 0x97, 0x57, 0x47, 0x42, 0xdc, 0x81, 0x10, 0xff, 0x3c, 0x9b, 0xf8, 0x4e, + 0xed, 0x48, 0x88, 0x3b, 0x99, 0x2d, 0x1e, 0x43, 0x03, 0x9a, 0xbd, 0xe6, 0x4e, 0xc0, 0x9e, 0x85, + 0xcd, 0x15, 0x30, 0x51, 0x3b, 0xf6, 0x9a, 0x5b, 0x87, 0xdd, 0x88, 0x16, 0x12, 0xa8, 0xdf, 0xcc, + 0x76, 0x79, 0xc9, 0x77, 0x24, 0xf8, 0xb7, 0x73, 0x89, 0xfc, 0xd5, 0xac, 0xf4, 0x52, 0xf0, 0x9e, + 0x4e, 0x12, 0x5e, 0x6b, 0xac, 0x90, 0x96, 0x1e, 0x4d, 0x12, 0xee, 0x02, 0x94, 0xe7, 0x18, 0x0d, + 0x49, 0xd4, 0x2f, 0x65, 0x23, 0x4f, 0x25, 0x8f, 0x64, 0xb7, 0x6d, 0xd9, 0x05, 0x5a, 0xc7, 0x5f, + 0x7f, 0x1e, 0x49, 0x6e, 0xbb, 0x92, 0xfb, 0x44, 0x36, 0xf2, 0x50, 0xf6, 0xde, 0xcd, 0x17, 0xfc, + 0xa5, 0x6c, 0xfc, 0xd1, 0xef, 0xbd, 0xab, 0x49, 0x8f, 0xa1, 0x01, 0x2e, 0x87, 0x60, 0xa9, 0x60, + 0xf3, 0x3e, 0x03, 0xc2, 0x01, 0x6a, 0x40, 0xa0, 0x7e, 0x47, 0x16, 0xc9, 0x0f, 0x98, 0xef, 0x51, + 0x1d, 0xfa, 0xd5, 0xac, 0xfc, 0x74, 0xfb, 0xde, 0xd5, 0x9f, 0xcb, 0x08, 0xd5, 0x3a, 0x8b, 0x0d, + 0x1e, 0xf9, 0xb3, 0x57, 0x38, 0x81, 0x0f, 0xa0, 0x9a, 0x40, 0xa1, 0xfe, 0xcf, 0x6c, 0xe2, 0x7b, + 0xf2, 0x7b, 0x57, 0x80, 0x4f, 0xc1, 0xa9, 0x78, 0xc3, 0x0a, 0x27, 0x72, 0x38, 0x84, 0xa4, 0xe3, + 0x2f, 0x96, 0xda, 0xcc, 0x27, 0xc4, 0xef, 0x4b, 0x30, 0xd7, 0x20, 0x70, 0x7c, 0x68, 0xae, 0x89, + 0x37, 0x0c, 0x82, 0xe1, 0xf6, 0x3b, 0xd9, 0xad, 0x9e, 0xdf, 0xdf, 0xcb, 0xab, 0x6a, 0x5f, 0x55, + 0x5f, 0x87, 0x30, 0x71, 0xb4, 0x27, 0x86, 0x58, 0xe2, 0xad, 0x36, 0x03, 0x89, 0x37, 0x62, 0x9c, + 0x4a, 0xfd, 0xd3, 0xde, 0xe4, 0xb7, 0xdf, 0xf7, 0xae, 0x08, 0xcf, 0xa2, 0x7c, 0x55, 0xf7, 0x56, + 0xb8, 0x26, 0xc3, 0x6d, 0x5d, 0x5b, 0xf7, 0x56, 0x34, 0x80, 0xe2, 0x4b, 0xa8, 0x5f, 0xd3, 0xd7, + 0xd8, 0x99, 0x67, 0x21, 0x4c, 0x8a, 0xe6, 0xe8, 0x6b, 0x75, 0x76, 0xee, 0x19, 0xa0, 0xb1, 0x1a, + 0x24, 0xe5, 0x63, 0x27, 0xdf, 0x90, 0xd1, 0x8a, 0x25, 0xe5, 0x0b, 0x52, 0xf1, 0x9d, 0x45, 0xf9, + 0x71, 0xdb, 0x58, 0x07, 0x67, 0x96, 0x21, 0x56, 0xd9, 0xa2, 0x6d, 0xac, 0x6b, 0x00, 0xc5, 0x9f, + 0xcc, 0xa0, 0xbe, 0x69, 0xa2, 0x1b, 0x74, 0x84, 0x0c, 0x74, 0xf3, 0x05, 0xf9, 0xc0, 0xfe, 0xf8, + 0x82, 0x8c, 0xad, 0xb0, 0xca, 0x44, 0x45, 0xe1, 0xf5, 0xe3, 0xeb, 0xa8, 0x7f, 0x42, 0xf7, 0xc8, + 0xb2, 0xed, 0xac, 0x83, 0x77, 0xcb, 0x48, 0xe8, 0xe9, 0x2c, 0xe9, 0x8f, 0x4f, 0xc4, 0x6e, 0xc6, + 0x1a, 0xfc, 0x97, 0x16, 0x14, 0xa6, 0x62, 0xe1, 0xc9, 0xba, 0x07, 0x43, 0xb1, 0xb0, 0xac, 0xdc, + 0x41, 0x4e, 0xee, 0xe0, 0x58, 0x79, 0x28, 0xf9, 0x58, 0x19, 0xac, 0x47, 0xf0, 0x80, 0x83, 0x54, + 0x78, 0xc3, 0xb0, 0xe8, 0x33, 0xeb, 0x11, 0xa0, 0x90, 0x09, 0x4f, 0x13, 0x48, 0xd4, 0xaf, 0xf5, + 0xa2, 0xc4, 0x97, 0xa2, 0x47, 0x4a, 0x7e, 0xa4, 0xe4, 0xa1, 0x92, 0x97, 0x63, 0x4a, 0x7e, 0x26, + 0xfe, 0xf6, 0xf8, 0x1d, 0xaa, 0xe1, 0x3f, 0x98, 0x8f, 0x45, 0x2e, 0xb8, 0xb7, 0x77, 0x97, 0xa1, + 0xf4, 0x7a, 0xb7, 0x94, 0x5e, 0x30, 0x20, 0x0a, 0x5b, 0x0e, 0x88, 0xbe, 0xed, 0x0e, 0x88, 0xfe, + 0xd4, 0x01, 0x11, 0x2a, 0xc8, 0x40, 0xaa, 0x82, 0x54, 0xf8, 0xa0, 0x41, 0xdd, 0x13, 0x28, 0x9c, + 0xdd, 0xdc, 0x28, 0x8e, 0xd0, 0xd1, 0x94, 0x98, 0x39, 0x01, 0x58, 0xa8, 0x5f, 0xcd, 0x77, 0x09, + 0x37, 0x72, 0x20, 0x3a, 0xf2, 0x14, 0xca, 0x95, 0xda, 0x6d, 0xae, 0x1f, 0xc7, 0x85, 0x48, 0x27, + 0x29, 0xa5, 0x28, 0x35, 0x7e, 0x1e, 0xe5, 0x4a, 0xb7, 0x6b, 0xd1, 0xa4, 0x09, 0xa5, 0xdb, 0x35, + 0xfe, 0x25, 0xa9, 0x65, 0x6f, 0xd7, 0xf0, 0x8b, 0x61, 0xf4, 0xc2, 0x95, 0x8e, 0xb5, 0xca, 0x37, + 0x8a, 0xdc, 0x09, 0xd6, 0xf7, 0xb4, 0x69, 0x50, 0x14, 0xdd, 0x2e, 0x46, 0x68, 0x23, 0xda, 0x54, + 0xd8, 0xbe, 0x36, 0xf5, 0x6d, 0xa9, 0x4d, 0xfd, 0xdb, 0xd5, 0xa6, 0x81, 0x6d, 0x68, 0x13, 0xda, + 0x52, 0x9b, 0x06, 0xf7, 0xae, 0x4d, 0x6d, 0x74, 0x26, 0x1e, 0x22, 0x2a, 0xd0, 0x08, 0x0d, 0xe1, + 0x38, 0x96, 0x3b, 0x96, 0xc0, 0xd5, 0x7f, 0x87, 0x61, 0xeb, 0x2c, 0xa9, 0x7e, 0x34, 0x25, 0xbd, + 0x96, 0x50, 0x5a, 0xfd, 0xa5, 0x6c, 0x7a, 0x64, 0xab, 0xc3, 0x39, 0xc5, 0x7d, 0x4b, 0xa2, 0x94, + 0xf2, 0xf2, 0x4b, 0xe3, 0x74, 0x29, 0x47, 0xd8, 0x26, 0xc9, 0xec, 0x2b, 0x99, 0xb4, 0x70, 0x5b, + 0x7b, 0x92, 0xd8, 0x23, 0x71, 0x67, 0x35, 0xf0, 0x9e, 0x77, 0x65, 0x2f, 0xb5, 0x68, 0x8e, 0xf6, + 0xdc, 0x2e, 0x73, 0xb4, 0xff, 0x66, 0x06, 0x1d, 0xbf, 0xd1, 0x59, 0x24, 0xdc, 0x39, 0x2d, 0x68, + 0xc6, 0x1b, 0x08, 0x51, 0x30, 0x77, 0x62, 0xc9, 0x80, 0x13, 0xcb, 0x7b, 0xc5, 0x50, 0x59, 0x91, + 0x02, 0x97, 0x43, 0x6a, 0xe6, 0xc0, 0x72, 0xce, 0x77, 0xb1, 0x5c, 0xed, 0x2c, 0x92, 0x7a, 0xcc, + 0x93, 0x45, 0xe0, 0x7e, 0xe6, 0x25, 0xe6, 0xbc, 0xbe, 0x5b, 0xa7, 0x91, 0x5f, 0xc8, 0xa6, 0x46, + 0x27, 0x3b, 0xb4, 0x69, 0xf4, 0x3e, 0x94, 0xd8, 0x2b, 0xd1, 0x74, 0x7a, 0x09, 0x24, 0x11, 0x8e, + 0x49, 0x5c, 0x92, 0x05, 0x76, 0xc8, 0x93, 0x3b, 0xbe, 0xad, 0x02, 0xfb, 0xfd, 0x4c, 0x6a, 0x14, + 0xb9, 0xc3, 0x2a, 0x30, 0xf5, 0x3f, 0xe4, 0xfc, 0xe0, 0x75, 0x7b, 0xfa, 0x84, 0xc7, 0xd0, 0x00, + 0xcf, 0xd0, 0x23, 0xfb, 0xd6, 0xf2, 0xa3, 0x3c, 0x38, 0x1a, 0x0e, 0x08, 0xe8, 0x32, 0xef, 0x07, + 0xd7, 0x0a, 0xb2, 0xfa, 0xc3, 0x32, 0x6f, 0x72, 0x28, 0xa5, 0x17, 0x48, 0xe8, 0x42, 0x3e, 0x79, + 0xd7, 0xf4, 0xc0, 0x2a, 0xa0, 0x7d, 0x99, 0x63, 0x0b, 0x39, 0xb9, 0x6b, 0x7a, 0xcc, 0x26, 0x08, + 0xd0, 0x74, 0x91, 0xae, 0x85, 0xa9, 0xab, 0xf9, 0x22, 0xed, 0xf2, 0x0c, 0xde, 0xfc, 0x31, 0xd7, + 0x63, 0x68, 0x80, 0x3b, 0xac, 0x72, 0x37, 0x13, 0xde, 0x5a, 0xee, 0xe2, 0x0a, 0xad, 0x0d, 0x08, + 0x28, 0x47, 0x8d, 0x2c, 0x87, 0x8e, 0x75, 0xc0, 0xd1, 0x01, 0x88, 0xc6, 0x31, 0xf8, 0x2a, 0x1a, + 0xa9, 0x79, 0xba, 0x65, 0xe8, 0x8e, 0x31, 0xdf, 0xf1, 0xda, 0x1d, 0x4f, 0x34, 0x4a, 0x5d, 0xcf, + 0xb0, 0x3b, 0x9e, 0x16, 0xa1, 0xc0, 0x4f, 0xa0, 0x61, 0x1f, 0x32, 0xe9, 0x38, 0xb6, 0x23, 0x5a, + 0x1e, 0xae, 0x67, 0x10, 0xc7, 0xd1, 0x64, 0x02, 0xfc, 0x3e, 0x34, 0x5c, 0xb1, 0xee, 0xd8, 0x0d, + 0xf6, 0xe2, 0x56, 0x9b, 0xe1, 0x76, 0x08, 0x3c, 0x90, 0x32, 0x03, 0x44, 0xbd, 0xe3, 0x34, 0x35, + 0x99, 0x50, 0xdd, 0xcc, 0xc6, 0x63, 0xfc, 0xdd, 0xbb, 0x9b, 0x96, 0x4b, 0xb2, 0x33, 0x1d, 0x78, + 0x90, 0x82, 0x41, 0x28, 0xfa, 0xf2, 0x32, 0xbb, 0xf0, 0x2a, 0xea, 0xbf, 0x41, 0xd6, 0x99, 0xdf, + 0x67, 0x21, 0x74, 0x15, 0x5e, 0xe5, 0x30, 0xf1, 0xc4, 0xd5, 0xa7, 0x53, 0xbf, 0x9c, 0x8d, 0x47, + 0x2f, 0xbc, 0x77, 0x85, 0xfd, 0x04, 0xea, 0x03, 0x51, 0x56, 0xfc, 0x23, 0x7f, 0x10, 0x20, 0x88, + 0x5b, 0xf6, 0x40, 0xf6, 0xc9, 0xd4, 0x1f, 0x2f, 0x44, 0x43, 0x5a, 0xde, 0xbb, 0xd2, 0x7b, 0x01, + 0x0d, 0x4e, 0xd8, 0x96, 0x6b, 0xba, 0x1e, 0xb1, 0x1a, 0xbe, 0xc2, 0x9e, 0xa6, 0x06, 0x55, 0x23, + 0x04, 0x8b, 0x2f, 0x83, 0x04, 0xea, 0xdd, 0x28, 0x2f, 0x7e, 0x06, 0x0d, 0x80, 0xc8, 0xc1, 0x4f, + 0x9a, 0x4d, 0x78, 0x70, 0x5b, 0xb0, 0x48, 0x81, 0x51, 0x27, 0xe9, 0x90, 0x14, 0xdf, 0x44, 0xfd, + 0x13, 0x2b, 0x66, 0xd3, 0x70, 0x88, 0x05, 0xfe, 0xc2, 0x42, 0x8c, 0x03, 0xb9, 0x2f, 0x2f, 0xc3, + 0xbf, 0x40, 0xcb, 0x9a, 0xd3, 0xe0, 0xc5, 0xa4, 0xb7, 0x51, 0x1c, 0x76, 0xe6, 0xfb, 0xb3, 0x08, + 0x85, 0x05, 0xf0, 0x03, 0x28, 0x1b, 0x64, 0x85, 0x05, 0x37, 0x15, 0x49, 0x83, 0xb2, 0xb0, 0x54, + 0xf0, 0xb1, 0x9d, 0xdd, 0x72, 0x6c, 0xdf, 0x44, 0x05, 0x76, 0xe2, 0x05, 0x9e, 0xe4, 0x42, 0x94, + 0xbd, 0xd4, 0x06, 0x5f, 0x06, 0x7a, 0xb6, 0x99, 0x05, 0xcb, 0x53, 0xf2, 0xca, 0x66, 0xcc, 0xce, + 0x34, 0x50, 0x2f, 0xfc, 0x85, 0x2f, 0xa0, 0xfc, 0x82, 0x9f, 0x51, 0x72, 0x98, 0xcd, 0xd2, 0x11, + 0xf9, 0x01, 0x9e, 0x76, 0xd3, 0x84, 0x6d, 0x79, 0xb4, 0x6a, 0x68, 0xf5, 0x10, 0x97, 0x0b, 0x87, + 0x49, 0x72, 0xe1, 0x30, 0xf5, 0x9f, 0x66, 0x13, 0x82, 0xad, 0xde, 0xbb, 0xc3, 0xe4, 0x39, 0x84, + 0xe0, 0xa1, 0x35, 0x95, 0xa7, 0xff, 0x44, 0x03, 0x46, 0x09, 0x30, 0x02, 0xb5, 0x95, 0xb6, 0x1d, + 0x21, 0xb1, 0xfa, 0xdb, 0x99, 0x58, 0x84, 0xce, 0x3d, 0xc9, 0x51, 0xb4, 0xca, 0xb2, 0xbb, 0x34, + 0x63, 0xfd, 0xbe, 0xc8, 0xed, 0xac, 0x2f, 0xe4, 0x6f, 0xd9, 0x07, 0xcb, 0xf4, 0x20, 0xbf, 0xe5, + 0x6b, 0xd9, 0xa4, 0x78, 0xa5, 0x87, 0x53, 0xc5, 0xaf, 0x05, 0x46, 0x69, 0x3e, 0x12, 0x21, 0x1a, + 0xa0, 0xd1, 0xac, 0xb7, 0xdc, 0x4c, 0xfd, 0x28, 0x3a, 0x16, 0x89, 0xe2, 0xc9, 0x13, 0x90, 0x5e, + 0xe8, 0x1e, 0x0e, 0x34, 0xfd, 0x89, 0xbe, 0x44, 0xa6, 0xfe, 0xaf, 0x4c, 0xf7, 0x18, 0xae, 0x07, + 0xae, 0x3a, 0x09, 0x02, 0xc8, 0xfd, 0xf5, 0x08, 0x60, 0x1f, 0xb6, 0xc1, 0x87, 0x5b, 0x00, 0xef, + 0x90, 0xc9, 0xe3, 0xed, 0x16, 0xc0, 0x8f, 0x67, 0xb6, 0x0c, 0xc1, 0x7b, 0xd0, 0x32, 0x50, 0xff, + 0x6d, 0x26, 0x31, 0x54, 0xee, 0x9e, 0xda, 0xf5, 0x22, 0x2a, 0x30, 0xb7, 0x1a, 0xde, 0x2a, 0x21, + 0xb9, 0x10, 0x85, 0xa6, 0xa5, 0xe3, 0x66, 0x58, 0x3c, 0x83, 0xfa, 0x58, 0x1b, 0x0c, 0xde, 0x1b, + 0x0f, 0x77, 0x89, 0xd7, 0x6b, 0xa4, 0x4d, 0x8e, 0x1c, 0xad, 0xfe, 0x56, 0x26, 0x16, 0xb9, 0xf7, + 0x00, 0xbf, 0x2d, 0x9c, 0xaa, 0x73, 0xdb, 0x9f, 0xaa, 0xd5, 0x3f, 0xc9, 0x26, 0x07, 0x0e, 0x3e, + 0xc0, 0x0f, 0xd9, 0x8f, 0xe3, 0xb4, 0xdd, 0xad, 0x5b, 0x0b, 0x68, 0x44, 0x96, 0x05, 0x5f, 0xb6, + 0xce, 0x27, 0x87, 0x4f, 0x4e, 0x69, 0x45, 0x84, 0x87, 0xfa, 0x56, 0x26, 0x1e, 0xf3, 0xf8, 0xc0, + 0xe7, 0xa7, 0xdd, 0x69, 0x8b, 0xfc, 0x29, 0xef, 0x90, 0xb5, 0x66, 0x3f, 0x3e, 0xe5, 0x1d, 0xb2, + 0x6a, 0xec, 0xee, 0x53, 0x7e, 0x36, 0x9b, 0x16, 0x32, 0xfa, 0xc0, 0x3f, 0xe8, 0x83, 0xa2, 0x90, + 0x59, 0xcb, 0xf8, 0xa7, 0x3d, 0x90, 0x16, 0xa3, 0x39, 0x85, 0x67, 0x8c, 0xcf, 0xee, 0xc6, 0x78, + 0xa2, 0xb0, 0xde, 0x21, 0x8a, 0x7c, 0x38, 0x84, 0xf5, 0x0e, 0x19, 0x2a, 0xef, 0x3c, 0x61, 0xfd, + 0x7a, 0x76, 0xbb, 0x71, 0xca, 0x8f, 0x84, 0x17, 0x13, 0xde, 0x67, 0xb3, 0xf1, 0xf8, 0xf9, 0x07, + 0x2e, 0xa6, 0x29, 0x54, 0xe0, 0x91, 0xfc, 0x53, 0x85, 0xc3, 0xf0, 0x69, 0x16, 0x0d, 0xff, 0x8e, + 0x6b, 0x88, 0x5f, 0xe4, 0x6c, 0x4f, 0x24, 0x8c, 0x56, 0xfd, 0x66, 0x26, 0x12, 0x6c, 0xfe, 0x40, + 0x8e, 0x10, 0x76, 0xb5, 0x24, 0xe1, 0x97, 0xfc, 0xc3, 0xcc, 0x7c, 0x24, 0x52, 0x70, 0xf0, 0x3d, + 0x65, 0xe2, 0xe9, 0x66, 0x33, 0x5a, 0x9e, 0xc7, 0x04, 0xf8, 0x72, 0x16, 0x8d, 0xc5, 0x48, 0xf1, + 0x05, 0x29, 0x4a, 0x0e, 0x1c, 0x4b, 0x46, 0x9c, 0xc7, 0x59, 0xbc, 0x9c, 0x1d, 0x9c, 0xa4, 0x5e, + 0x40, 0xf9, 0xb2, 0xbe, 0xce, 0xbe, 0xad, 0x97, 0xb1, 0x34, 0xf4, 0x75, 0xf1, 0xc4, 0x0d, 0xf0, + 0x78, 0x11, 0xdd, 0xc7, 0xee, 0x43, 0x4c, 0xdb, 0x5a, 0x30, 0x5b, 0xa4, 0x62, 0xcd, 0x9a, 0xcd, + 0xa6, 0xe9, 0xf2, 0x4b, 0xbd, 0xc7, 0x36, 0x37, 0x8a, 0x17, 0x3d, 0xdb, 0xd3, 0x9b, 0x75, 0xe2, + 0x93, 0xd5, 0x3d, 0xb3, 0x45, 0xea, 0xa6, 0x55, 0x6f, 0x01, 0xa5, 0xc0, 0x32, 0x99, 0x15, 0xae, + 0xb0, 0xa0, 0xd0, 0xb5, 0x86, 0x6e, 0x59, 0xc4, 0xa8, 0x58, 0xe3, 0xeb, 0x1e, 0x61, 0x97, 0x81, + 0x39, 0x76, 0x24, 0xc8, 0xde, 0x86, 0x33, 0x34, 0x65, 0xbc, 0x48, 0x09, 0xb4, 0x84, 0x42, 0xea, + 0x6f, 0xe4, 0x13, 0xf2, 0x0c, 0x1c, 0x22, 0xf5, 0xf1, 0x7b, 0x3a, 0xbf, 0x45, 0x4f, 0x5f, 0x41, + 0x7d, 0x3c, 0xc4, 0x27, 0xbf, 0x60, 0x00, 0x67, 0xf6, 0x3b, 0x0c, 0x24, 0xde, 0xd0, 0x70, 0x2a, + 0xdc, 0x44, 0x67, 0x16, 0x68, 0x37, 0x25, 0x77, 0x66, 0x61, 0x17, 0x9d, 0xd9, 0x85, 0x1f, 0x7e, + 0x1d, 0x9d, 0x02, 0x6c, 0x42, 0xb7, 0xf6, 0x41, 0x55, 0x10, 0x39, 0x8a, 0x55, 0x95, 0xdc, 0xb9, + 0x69, 0xe5, 0xf1, 0x07, 0xd1, 0x50, 0x30, 0x40, 0x4c, 0xe2, 0xf2, 0x9b, 0x8b, 0x2e, 0xe3, 0x8c, + 0x85, 0x65, 0xa3, 0x60, 0x70, 0x21, 0x93, 0x43, 0x7b, 0x49, 0xbc, 0xd4, 0x7f, 0x93, 0xe9, 0x96, + 0xef, 0xe0, 0xc0, 0x67, 0xe5, 0x97, 0x50, 0x9f, 0xc1, 0x3e, 0x8a, 0xeb, 0x54, 0xf7, 0x8c, 0x08, + 0x8c, 0x54, 0xf3, 0xcb, 0xa8, 0x7f, 0x9c, 0xe9, 0x9a, 0x66, 0xe1, 0xb0, 0x7f, 0xde, 0x67, 0x73, + 0x29, 0x9f, 0xc7, 0x27, 0xd1, 0x4b, 0x68, 0xd4, 0x0c, 0x23, 0x56, 0xd7, 0xc3, 0xf0, 0x53, 0xda, + 0x31, 0x01, 0x0e, 0xa3, 0xeb, 0x1a, 0x3a, 0xe9, 0x3b, 0x3e, 0x3a, 0xbe, 0x87, 0x98, 0x5b, 0xef, + 0x38, 0x26, 0x1b, 0x97, 0xda, 0x09, 0x37, 0xe2, 0x3e, 0xe6, 0xde, 0x74, 0x4c, 0x5a, 0x81, 0xee, + 0xad, 0x10, 0x4b, 0xaf, 0xaf, 0xd9, 0xce, 0x2a, 0xc4, 0xfe, 0x64, 0x83, 0x53, 0x3b, 0xc6, 0xe0, + 0xb7, 0x7d, 0x30, 0x7e, 0x08, 0x0d, 0x2f, 0x37, 0x3b, 0x24, 0x88, 0xb6, 0xc8, 0xee, 0xfa, 0xb4, + 0x21, 0x0a, 0x0c, 0x6e, 0x48, 0xce, 0x21, 0x04, 0x44, 0x1e, 0x24, 0xc1, 0x80, 0x8b, 0x3d, 0x6d, + 0x80, 0x42, 0x16, 0x78, 0x77, 0x9d, 0x61, 0x5a, 0xcd, 0x84, 0x54, 0x6f, 0xda, 0xd6, 0x72, 0xdd, + 0x23, 0x4e, 0x0b, 0x1a, 0x0a, 0xce, 0x0c, 0xda, 0x49, 0xa0, 0x80, 0xab, 0x13, 0x77, 0xc6, 0xb6, + 0x96, 0x17, 0x88, 0xd3, 0xa2, 0x4d, 0x7d, 0x0c, 0x61, 0xde, 0x54, 0x07, 0x0e, 0x3d, 0xd8, 0xc7, + 0x81, 0x37, 0x83, 0xc6, 0x3f, 0x82, 0x9d, 0x86, 0xc0, 0x87, 0x15, 0xd1, 0x20, 0x0b, 0x39, 0xc7, + 0x84, 0x06, 0x2e, 0x0c, 0x1a, 0x62, 0x20, 0x90, 0xd7, 0x49, 0xc4, 0xbd, 0x2b, 0x98, 0x57, 0xb7, + 0xc6, 0x7f, 0xa9, 0x9f, 0xca, 0x25, 0x65, 0x86, 0xd8, 0x93, 0xa2, 0x85, 0xd3, 0x6a, 0x76, 0x47, + 0xd3, 0xea, 0x31, 0xab, 0xd3, 0xaa, 0xeb, 0xed, 0x76, 0x7d, 0xc9, 0x6c, 0xc2, 0xb3, 0x2a, 0x58, + 0xf8, 0xb4, 0x61, 0xab, 0xd3, 0x2a, 0xb5, 0xdb, 0x53, 0x0c, 0x88, 0x1f, 0x45, 0x63, 0x94, 0x0e, + 0x3a, 0x29, 0xa0, 0xcc, 0x03, 0x25, 0x65, 0x00, 0x31, 0x5b, 0x7d, 0xda, 0xd3, 0xa8, 0x9f, 0xf3, + 0x64, 0x6b, 0x55, 0xaf, 0xd6, 0xc7, 0x98, 0xb9, 0xb4, 0xe7, 0x02, 0x36, 0x6c, 0x72, 0xed, 0xd5, + 0x06, 0xfc, 0xf2, 0x10, 0x99, 0xd8, 0xea, 0xb4, 0x58, 0x44, 0xac, 0x3e, 0x40, 0x06, 0xbf, 0xf1, + 0x05, 0x34, 0x42, 0xb9, 0x04, 0x02, 0x63, 0xc1, 0x5c, 0x7b, 0xb5, 0x08, 0x14, 0x5f, 0x45, 0x27, + 0x24, 0x08, 0xb3, 0x41, 0xd9, 0x33, 0x81, 0x5e, 0x2d, 0x11, 0xa7, 0xbe, 0x95, 0x8b, 0x27, 0xbe, + 0x38, 0x90, 0xb5, 0x71, 0x1a, 0x21, 0x9e, 0xd7, 0x26, 0xbc, 0xa0, 0x09, 0xbc, 0x96, 0x43, 0x4c, + 0x0a, 0x0f, 0xa1, 0x2c, 0xbe, 0x84, 0xfa, 0xd9, 0x17, 0x55, 0xca, 0x7c, 0xcd, 0x04, 0x37, 0x23, + 0xb7, 0x6d, 0x2e, 0x2d, 0x81, 0x4f, 0x52, 0x80, 0xc6, 0x17, 0x50, 0x5f, 0x79, 0xae, 0x56, 0x2b, + 0xcd, 0xf9, 0xb7, 0x8d, 0xf0, 0x46, 0xc1, 0xb0, 0xdc, 0xba, 0xab, 0x5b, 0xae, 0xe6, 0x23, 0xf1, + 0x43, 0xa8, 0x50, 0xa9, 0x02, 0x19, 0x7b, 0x79, 0x37, 0xb8, 0xb9, 0x51, 0xec, 0x33, 0xdb, 0x8c, + 0x8a, 0xa3, 0xa0, 0xde, 0x5b, 0x95, 0xb2, 0x70, 0xe5, 0xce, 0xea, 0xbd, 0x63, 0x1a, 0x70, 0x75, + 0xa9, 0x05, 0x68, 0xfc, 0x34, 0x1a, 0xaa, 0x11, 0xc7, 0xd4, 0x9b, 0x73, 0x1d, 0xd8, 0x6e, 0x30, + 0x37, 0xa3, 0xb1, 0xcd, 0x8d, 0xe2, 0xb0, 0x0b, 0xf0, 0xba, 0x05, 0x08, 0x4d, 0x22, 0xc3, 0x67, + 0x51, 0x7e, 0xda, 0xb4, 0x7c, 0x37, 0x78, 0xf0, 0x93, 0x5e, 0x31, 0x2d, 0x4f, 0x03, 0xa8, 0xfa, + 0x5f, 0xb2, 0xc9, 0xa9, 0x47, 0x0e, 0x60, 0x6c, 0xed, 0xf2, 0xb6, 0x30, 0xa2, 0x04, 0xf9, 0x3d, + 0x28, 0xc1, 0x12, 0x3a, 0x56, 0x32, 0x5a, 0xa6, 0x55, 0x82, 0x9f, 0xee, 0xec, 0x54, 0x09, 0x06, + 0xa4, 0xf0, 0x0c, 0x2b, 0x82, 0xe6, 0xdf, 0xc3, 0xc2, 0xad, 0x52, 0x54, 0x5d, 0x67, 0xb8, 0x7a, + 0x6b, 0x49, 0xaf, 0x37, 0x58, 0xd6, 0x0e, 0x2d, 0xca, 0x54, 0xfd, 0xbe, 0xec, 0x16, 0xd9, 0x52, + 0xee, 0x45, 0xe9, 0xab, 0x9f, 0xcb, 0x76, 0x4f, 0x58, 0x73, 0x4f, 0x0a, 0xe5, 0xcf, 0xb3, 0x09, + 0xe9, 0x63, 0xf6, 0x24, 0x89, 0x4b, 0xa8, 0x9f, 0xb1, 0x09, 0xdc, 0x35, 0x61, 0xc6, 0x61, 0xca, + 0x0a, 0x33, 0x9d, 0x8f, 0xc6, 0x73, 0xe8, 0x44, 0x69, 0x69, 0x89, 0x34, 0xbc, 0x30, 0xf0, 0xee, + 0x5c, 0x18, 0x6c, 0x93, 0x45, 0x2b, 0xe5, 0xf8, 0x30, 0x70, 0x2f, 0x04, 0x95, 0x48, 0x2c, 0x87, + 0x17, 0xd0, 0xc9, 0x28, 0xbc, 0xc6, 0x4c, 0xbd, 0xbc, 0x10, 0xc0, 0x34, 0xc6, 0x91, 0xfd, 0xa7, + 0xa5, 0x94, 0x4d, 0x6a, 0x25, 0x4c, 0xa7, 0xbd, 0xdd, 0x5a, 0x09, 0x73, 0x6b, 0x62, 0x39, 0xf5, + 0xcb, 0x39, 0x31, 0xcb, 0xce, 0xbd, 0xeb, 0x58, 0x73, 0x4d, 0x72, 0xa7, 0xdd, 0xee, 0x90, 0x79, + 0x9a, 0x47, 0x8a, 0x30, 0x3a, 0x8e, 0xef, 0x79, 0x16, 0xbc, 0x54, 0x07, 0xa0, 0xe8, 0x43, 0x16, + 0x50, 0xe2, 0x0a, 0xca, 0x97, 0x9c, 0x65, 0x66, 0xc6, 0x6c, 0xf5, 0x78, 0x46, 0x77, 0x96, 0xdd, + 0xe4, 0xc7, 0x33, 0x94, 0x85, 0xfa, 0xbd, 0xd9, 0x2e, 0x59, 0x78, 0xee, 0xc9, 0x49, 0xe4, 0x47, + 0xb2, 0x69, 0xf9, 0x74, 0x0e, 0xab, 0x8b, 0xd0, 0xdb, 0x2c, 0x9c, 0xc3, 0xed, 0x3f, 0xb5, 0x8f, + 0xc2, 0xf9, 0xbd, 0x6c, 0x5a, 0x72, 0xa0, 0x23, 0xe1, 0xec, 0x6e, 0x82, 0x4c, 0x14, 0xe9, 0x3d, + 0x6c, 0x73, 0x8b, 0xaa, 0xd0, 0xbb, 0x4b, 0x37, 0x99, 0x24, 0x91, 0x1e, 0x0d, 0xe1, 0x3d, 0x69, + 0xe9, 0xef, 0x67, 0x53, 0x93, 0x60, 0x1d, 0xc9, 0x74, 0x3f, 0x65, 0x7a, 0x34, 0xf4, 0xf7, 0x34, + 0xf4, 0x13, 0x65, 0x7a, 0x34, 0xf6, 0xf7, 0xa4, 0xa7, 0xdf, 0x93, 0x8b, 0xa7, 0x79, 0xbb, 0x57, + 0x15, 0xd4, 0xd9, 0xa5, 0x82, 0xfa, 0xe5, 0xf0, 0xcb, 0xe8, 0x58, 0x28, 0x4b, 0x31, 0xdc, 0x0b, + 0x5c, 0xb4, 0x35, 0x28, 0xaa, 0xfe, 0x06, 0xc5, 0xf1, 0xb8, 0x04, 0x51, 0x6a, 0xf5, 0x9b, 0xb9, + 0x78, 0xae, 0xbc, 0xa3, 0xde, 0xd8, 0x65, 0x6f, 0xdc, 0x44, 0x27, 0x27, 0x3a, 0x8e, 0x43, 0x2c, + 0x2f, 0xb9, 0x53, 0xe0, 0x24, 0xaf, 0xc1, 0x28, 0xea, 0xf1, 0xce, 0x49, 0x29, 0x4c, 0xd9, 0x72, + 0x97, 0xd6, 0x28, 0xdb, 0xbe, 0x90, 0x6d, 0x87, 0x51, 0x24, 0xb1, 0x4d, 0x2e, 0xac, 0xfe, 0x4e, + 0x36, 0x9e, 0xdd, 0xf0, 0xa8, 0xeb, 0x77, 0xd7, 0xf5, 0x8f, 0xce, 0xb2, 0x6c, 0x31, 0x37, 0x4c, + 0xcb, 0xc0, 0xa7, 0xd1, 0x7d, 0x37, 0x6b, 0x93, 0x5a, 0xfd, 0x46, 0x65, 0xae, 0x5c, 0xbf, 0x39, + 0x57, 0xab, 0x4e, 0x4e, 0x54, 0xa6, 0x2a, 0x93, 0xe5, 0xd1, 0x1e, 0x7c, 0x1c, 0x1d, 0x0b, 0x51, + 0xd3, 0x37, 0x67, 0x4b, 0x73, 0xa3, 0x19, 0x3c, 0x86, 0x86, 0x43, 0xe0, 0xf8, 0xfc, 0xc2, 0x68, + 0xf6, 0xd1, 0xf7, 0xa0, 0x41, 0xb8, 0x0e, 0x63, 0xc7, 0xba, 0x78, 0x08, 0xf5, 0xcf, 0x8f, 0xd7, + 0x26, 0xb5, 0x5b, 0xc0, 0x04, 0xa1, 0x42, 0x79, 0x72, 0x8e, 0x32, 0xcc, 0x3c, 0xfa, 0xdf, 0x33, + 0x08, 0xd5, 0xa6, 0x16, 0xaa, 0x9c, 0x70, 0x10, 0xf5, 0x55, 0xe6, 0x6e, 0x95, 0x66, 0x2a, 0x94, + 0xae, 0x1f, 0xe5, 0xe7, 0xab, 0x93, 0xb4, 0x86, 0x01, 0xd4, 0x3b, 0x31, 0x33, 0x5f, 0x9b, 0x1c, + 0xcd, 0x52, 0xa0, 0x36, 0x59, 0x2a, 0x8f, 0xe6, 0x28, 0xf0, 0xb6, 0x56, 0x59, 0x98, 0x1c, 0xcd, + 0xd3, 0x3f, 0x67, 0x6a, 0x0b, 0xa5, 0x85, 0xd1, 0x5e, 0xfa, 0xe7, 0x14, 0xfc, 0x59, 0xa0, 0xcc, + 0x6a, 0x93, 0x0b, 0xf0, 0xa3, 0x8f, 0x36, 0x61, 0xca, 0xff, 0xd5, 0x4f, 0x51, 0x94, 0x75, 0xb9, + 0xa2, 0x8d, 0x0e, 0xd0, 0x1f, 0x94, 0x25, 0xfd, 0x81, 0x68, 0xe3, 0xb4, 0xc9, 0xd9, 0xf9, 0x5b, + 0x93, 0xa3, 0x83, 0x94, 0xd7, 0xec, 0x0d, 0x0a, 0x1e, 0xa2, 0x7f, 0x6a, 0xb3, 0xf4, 0xcf, 0x61, + 0xca, 0x49, 0x9b, 0x2c, 0xcd, 0x54, 0x4b, 0x0b, 0xd3, 0xa3, 0x23, 0xb4, 0x3d, 0xc0, 0xf3, 0x18, + 0x2b, 0x39, 0x57, 0x9a, 0x9d, 0x1c, 0x1d, 0xe5, 0x34, 0xe5, 0x99, 0xca, 0xdc, 0x8d, 0xd1, 0x31, + 0x68, 0xc8, 0xeb, 0xb3, 0xf0, 0x03, 0xd3, 0x02, 0xf0, 0xd7, 0xf1, 0x47, 0x3f, 0x8c, 0x0a, 0xf3, + 0x35, 0xb8, 0xbc, 0x38, 0x85, 0x8e, 0xcf, 0xd7, 0xea, 0x0b, 0xaf, 0x57, 0x27, 0x23, 0xf2, 0x1e, + 0x43, 0xc3, 0x3e, 0x62, 0xa6, 0x32, 0x77, 0xf3, 0x03, 0x4c, 0xda, 0x3e, 0x68, 0xb6, 0x34, 0x31, + 0x5f, 0x1b, 0xcd, 0xd2, 0x5e, 0xf1, 0x41, 0xb7, 0x2b, 0x73, 0xe5, 0xf9, 0xdb, 0xb5, 0xd1, 0xdc, + 0xa3, 0x77, 0xd0, 0x10, 0xcb, 0xb5, 0x33, 0xef, 0x98, 0xcb, 0xa6, 0x85, 0xcf, 0xa1, 0xd3, 0xe5, + 0xc9, 0x5b, 0x95, 0x89, 0xc9, 0xfa, 0xbc, 0x56, 0xb9, 0x5e, 0x99, 0x8b, 0xd4, 0x74, 0x1f, 0x1a, + 0x93, 0xd1, 0xa5, 0x6a, 0x65, 0x34, 0x83, 0x4f, 0x22, 0x2c, 0x83, 0x5f, 0x2d, 0xcd, 0x4e, 0x8d, + 0x66, 0xb1, 0x82, 0x4e, 0xc8, 0xf0, 0xca, 0xdc, 0xc2, 0xcd, 0xb9, 0xc9, 0xd1, 0xdc, 0xa3, 0x3f, + 0x99, 0x41, 0xf7, 0x25, 0xc6, 0x63, 0xc3, 0x2a, 0x3a, 0x3f, 0x39, 0x53, 0xaa, 0x2d, 0x54, 0x26, + 0x6a, 0x93, 0x25, 0x6d, 0x62, 0xba, 0x3e, 0x51, 0x5a, 0x98, 0xbc, 0x3e, 0xaf, 0xbd, 0x5e, 0xbf, + 0x3e, 0x39, 0x37, 0xa9, 0x95, 0x66, 0x46, 0x7b, 0xf0, 0x43, 0xa8, 0x98, 0x42, 0x53, 0x9b, 0x9c, + 0xb8, 0xa9, 0x55, 0x16, 0x5e, 0x1f, 0xcd, 0xe0, 0x07, 0xd1, 0xb9, 0x54, 0x22, 0xfa, 0x7b, 0x34, + 0x8b, 0xcf, 0xa3, 0x33, 0x69, 0x24, 0xaf, 0xcd, 0x8c, 0xe6, 0x1e, 0xfd, 0xa1, 0x0c, 0xc2, 0xf1, + 0x80, 0x5a, 0xf8, 0x01, 0x74, 0x96, 0xea, 0x45, 0x3d, 0xbd, 0x81, 0x0f, 0xa2, 0x73, 0x89, 0x14, + 0x42, 0xf3, 0x8a, 0xe8, 0xfe, 0x14, 0x12, 0xde, 0xb8, 0xb3, 0x48, 0x49, 0x26, 0x80, 0xa6, 0x7d, + 0x31, 0x83, 0xee, 0x4b, 0xbc, 0x49, 0xc1, 0x17, 0xd1, 0xc3, 0xa5, 0xf2, 0x2c, 0xed, 0x9b, 0x89, + 0x85, 0xca, 0xfc, 0x5c, 0xad, 0x3e, 0x3b, 0x55, 0xaa, 0x53, 0xed, 0xbb, 0x59, 0x8b, 0xf4, 0xe6, + 0x05, 0xa4, 0x76, 0xa1, 0x9c, 0x98, 0x2e, 0xcd, 0x5d, 0xa7, 0xc3, 0x0f, 0x3f, 0x8c, 0x1e, 0x48, + 0xa5, 0x9b, 0x9c, 0x2b, 0x8d, 0xcf, 0x4c, 0x96, 0x47, 0xb3, 0xf8, 0x11, 0xf4, 0x60, 0x2a, 0x55, + 0xb9, 0x52, 0x63, 0x64, 0xb9, 0xf1, 0xf2, 0x5b, 0xff, 0xee, 0x7c, 0xcf, 0x5b, 0x5f, 0x3f, 0x9f, + 0xf9, 0xbd, 0xaf, 0x9f, 0xcf, 0xfc, 0xc9, 0xd7, 0xcf, 0x67, 0x3e, 0x78, 0x75, 0x27, 0x81, 0xd2, + 0xd8, 0x94, 0xb5, 0x58, 0x80, 0x53, 0xcd, 0xa7, 0xfe, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xef, + 0x5a, 0x2a, 0xe3, 0xab, 0x69, 0x01, 0x00, } func (m *Metadata) Marshal() (dAtA []byte, err error) { @@ -35420,6 +35432,16 @@ func (m *AutoUpdateConfigCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -35487,6 +35509,16 @@ func (m *AutoUpdateConfigUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.ResourceMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -35554,6 +35586,16 @@ func (m *AutoUpdateConfigDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -35621,6 +35663,16 @@ func (m *AutoUpdateVersionCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -35688,6 +35740,16 @@ func (m *AutoUpdateVersionUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.ResourceMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -35755,6 +35817,16 @@ func (m *AutoUpdateVersionDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -44012,6 +44084,8 @@ func (m *AutoUpdateConfigCreate) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -44032,6 +44106,8 @@ func (m *AutoUpdateConfigUpdate) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.ResourceMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -44052,6 +44128,8 @@ func (m *AutoUpdateConfigDelete) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -44072,6 +44150,8 @@ func (m *AutoUpdateVersionCreate) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -44092,6 +44172,8 @@ func (m *AutoUpdateVersionUpdate) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.ResourceMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -44112,6 +44194,8 @@ func (m *AutoUpdateVersionDelete) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -98186,6 +98270,39 @@ func (m *AutoUpdateConfigCreate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -98369,6 +98486,39 @@ func (m *AutoUpdateConfigUpdate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -98552,6 +98702,39 @@ func (m *AutoUpdateConfigDelete) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -98735,6 +98918,39 @@ func (m *AutoUpdateVersionCreate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -98918,6 +99134,39 @@ func (m *AutoUpdateVersionUpdate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -99101,6 +99350,39 @@ func (m *AutoUpdateVersionDelete) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) diff --git a/api/types/events/oneof.go b/api/types/events/oneof.go index cc7fc838efbd4..d90028f01f602 100644 --- a/api/types/events/oneof.go +++ b/api/types/events/oneof.go @@ -727,6 +727,31 @@ func ToOneOf(in AuditEvent) (*OneOf, error) { out.Event = &OneOf_CrownJewelDelete{ CrownJewelDelete: e, } + case *AutoUpdateConfigCreate: + out.Event = &OneOf_AutoUpdateConfigCreate{ + AutoUpdateConfigCreate: e, + } + case *AutoUpdateConfigUpdate: + out.Event = &OneOf_AutoUpdateConfigUpdate{ + AutoUpdateConfigUpdate: e, + } + case *AutoUpdateConfigDelete: + out.Event = &OneOf_AutoUpdateConfigDelete{ + AutoUpdateConfigDelete: e, + } + + case *AutoUpdateVersionCreate: + out.Event = &OneOf_AutoUpdateVersionCreate{ + AutoUpdateVersionCreate: e, + } + case *AutoUpdateVersionUpdate: + out.Event = &OneOf_AutoUpdateVersionUpdate{ + AutoUpdateVersionUpdate: e, + } + case *AutoUpdateVersionDelete: + out.Event = &OneOf_AutoUpdateVersionDelete{ + AutoUpdateVersionDelete: e, + } default: log.Errorf("Attempted to convert dynamic event of unknown type \"%v\" into protobuf event.", in.GetType()) unknown := &Unknown{} diff --git a/lib/auth/autoupdate/autoupdatev1/service.go b/lib/auth/autoupdate/autoupdatev1/service.go index b14edeb13d6f9..e88ff95d87382 100644 --- a/lib/auth/autoupdate/autoupdatev1/service.go +++ b/lib/auth/autoupdate/autoupdatev1/service.go @@ -20,13 +20,16 @@ package autoupdatev1 import ( "context" + "log/slog" "github.com/gravitational/trace" "google.golang.org/protobuf/types/known/emptypb" "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" "github.com/gravitational/teleport/api/types" + apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/services" ) @@ -47,6 +50,8 @@ type ServiceConfig struct { Backend services.AutoUpdateService // Cache is the cache used to store AutoUpdate resources. Cache Cache + // Emitter is the event emitter. + Emitter apievents.Emitter } // Service implements the gRPC API layer for the AutoUpdate. @@ -55,6 +60,7 @@ type Service struct { authorizer authz.Authorizer backend services.AutoUpdateService + emitter apievents.Emitter cache Cache } @@ -67,11 +73,14 @@ func NewService(cfg ServiceConfig) (*Service, error) { return nil, trace.BadParameter("authorizer is required") case cfg.Cache == nil: return nil, trace.BadParameter("cache is required") + case cfg.Emitter == nil: + return nil, trace.BadParameter("Emitter is required") } return &Service{ authorizer: cfg.Authorizer, backend: cfg.Backend, cache: cfg.Cache, + emitter: cfg.Emitter, }, nil } @@ -110,6 +119,27 @@ func (s *Service) CreateAutoUpdateConfig(ctx context.Context, req *autoupdate.Cr } config, err := s.backend.CreateAutoUpdateConfig(ctx, req.Config) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateConfigCreate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateConfigCreateEvent, + Code: events.AutoUpdateConfigCreateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateConfig, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) return config, trace.Wrap(err) } @@ -129,6 +159,27 @@ func (s *Service) UpdateAutoUpdateConfig(ctx context.Context, req *autoupdate.Up } config, err := s.backend.UpdateAutoUpdateConfig(ctx, req.Config) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateConfigUpdate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateConfigUpdateEvent, + Code: events.AutoUpdateConfigUpdateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateConfig, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) return config, trace.Wrap(err) } @@ -148,6 +199,27 @@ func (s *Service) UpsertAutoUpdateConfig(ctx context.Context, req *autoupdate.Up } config, err := s.backend.UpsertAutoUpdateConfig(ctx, req.Config) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateConfigUpdate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateConfigUpdateEvent, + Code: events.AutoUpdateConfigUpdateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateConfig, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) return config, trace.Wrap(err) } @@ -166,10 +238,29 @@ func (s *Service) DeleteAutoUpdateConfig(ctx context.Context, req *autoupdate.De return nil, trace.Wrap(err) } - if err := s.backend.DeleteAutoUpdateConfig(ctx); err != nil { - return nil, trace.Wrap(err) - } - return &emptypb.Empty{}, nil + err = s.backend.DeleteAutoUpdateConfig(ctx) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateConfigDelete{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateConfigDeleteEvent, + Code: events.AutoUpdateConfigDeleteCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateConfig, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return &emptypb.Empty{}, trace.Wrap(err) } // GetAutoUpdateVersion gets the current AutoUpdateVersion singleton. @@ -207,6 +298,28 @@ func (s *Service) CreateAutoUpdateVersion(ctx context.Context, req *autoupdate.C } autoUpdateVersion, err := s.backend.CreateAutoUpdateVersion(ctx, req.Version) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateVersionCreate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateVersionCreateEvent, + Code: events.AutoUpdateVersionCreateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateVersion, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return autoUpdateVersion, trace.Wrap(err) } @@ -226,6 +339,28 @@ func (s *Service) UpdateAutoUpdateVersion(ctx context.Context, req *autoupdate.U } autoUpdateVersion, err := s.backend.UpdateAutoUpdateVersion(ctx, req.Version) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateVersionUpdate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateVersionUpdateEvent, + Code: events.AutoUpdateVersionUpdateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateVersion, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return autoUpdateVersion, trace.Wrap(err) } @@ -245,6 +380,28 @@ func (s *Service) UpsertAutoUpdateVersion(ctx context.Context, req *autoupdate.U } autoUpdateVersion, err := s.backend.UpsertAutoUpdateVersion(ctx, req.Version) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateVersionUpdate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateVersionUpdateEvent, + Code: events.AutoUpdateVersionUpdateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateVersion, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return autoUpdateVersion, trace.Wrap(err) } @@ -263,8 +420,36 @@ func (s *Service) DeleteAutoUpdateVersion(ctx context.Context, req *autoupdate.D return nil, trace.Wrap(err) } - if err := s.backend.DeleteAutoUpdateVersion(ctx); err != nil { - return nil, trace.Wrap(err) + err = s.backend.DeleteAutoUpdateVersion(ctx) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateVersionDelete{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateVersionDeleteEvent, + Code: events.AutoUpdateVersionDeleteCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateVersion, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return &emptypb.Empty{}, trace.Wrap(err) +} + +func (s *Service) emitEvent(ctx context.Context, e apievents.AuditEvent) { + if err := s.emitter.EmitAuditEvent(ctx, e); err != nil { + slog.WarnContext(ctx, "Failed to emit audit event", + "type", e.GetType(), + "error", err, + ) } - return &emptypb.Empty{}, nil } diff --git a/lib/auth/autoupdate/autoupdatev1/service_test.go b/lib/auth/autoupdate/autoupdatev1/service_test.go index 840fd9bbf94cd..eed042614cc37 100644 --- a/lib/auth/autoupdate/autoupdatev1/service_test.go +++ b/lib/auth/autoupdate/autoupdatev1/service_test.go @@ -25,10 +25,14 @@ import ( "github.com/gravitational/trace" "github.com/stretchr/testify/require" - "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" + autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/autoupdate" + apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/backend/memory" + libevents "github.com/gravitational/teleport/lib/events" + "github.com/gravitational/teleport/lib/events/eventstest" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/services/local" "github.com/gravitational/teleport/lib/utils" @@ -63,7 +67,7 @@ func otherAdminStates(states []authz.AdminActionAuthState) []authz.AdminActionAu // callMethod calls a method with given name in the DatabaseObjectService service func callMethod(t *testing.T, service *Service, method string) error { - for _, desc := range autoupdate.AutoUpdateService_ServiceDesc.Methods { + for _, desc := range autoupdatev1pb.AutoUpdateService_ServiceDesc.Methods { if desc.MethodName == method { _, err := desc.Handler(service, context.Background(), func(_ any) error { return nil }, nil) return err @@ -177,7 +181,7 @@ func TestServiceAccess(t *testing.T) { t.Run(stateToString(state), func(t *testing.T) { for _, verbs := range utils.Combinations(tt.allowedVerbs) { t.Run(fmt.Sprintf("verbs=%v", verbs), func(t *testing.T) { - service := newService(t, state, fakeChecker{allowedVerbs: verbs}) + service := newService(t, state, fakeChecker{allowedVerbs: verbs}, &libevents.DiscardEmitter{}) err := callMethod(t, service, tt.name) // expect access denied except with full set of verbs. if len(verbs) == len(tt.allowedVerbs) { @@ -201,7 +205,7 @@ func TestServiceAccess(t *testing.T) { t.Run(stateToString(state), func(t *testing.T) { // it is enough to test against tt.allowedVerbs, // this is the only different data point compared to the test cases above. - service := newService(t, state, fakeChecker{allowedVerbs: tt.allowedVerbs}) + service := newService(t, state, fakeChecker{allowedVerbs: tt.allowedVerbs}, &libevents.DiscardEmitter{}) err := callMethod(t, service, tt.name) require.True(t, trace.IsAccessDenied(err)) }) @@ -212,7 +216,7 @@ func TestServiceAccess(t *testing.T) { // verify that all declared methods have matching test cases t.Run("verify coverage", func(t *testing.T) { - for _, method := range autoupdate.AutoUpdateService_ServiceDesc.Methods { + for _, method := range autoupdatev1pb.AutoUpdateService_ServiceDesc.Methods { t.Run(method.MethodName, func(t *testing.T) { match := false for _, testCase := range testCases { @@ -224,6 +228,98 @@ func TestServiceAccess(t *testing.T) { }) } +func TestAutoUpdateConfigEvents(t *testing.T) { + rwVerbs := []string{types.VerbList, types.VerbCreate, types.VerbRead, types.VerbUpdate, types.VerbDelete} + mockEmitter := &eventstest.MockRecorderEmitter{} + service := newService(t, authz.AdminActionAuthMFAVerified, fakeChecker{allowedVerbs: rwVerbs}, mockEmitter) + ctx := context.Background() + + config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }) + require.NoError(t, err) + + _, err = service.CreateAutoUpdateConfig(ctx, &autoupdatev1pb.CreateAutoUpdateConfigRequest{Config: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateConfigCreateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateConfigCreateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateConfig, mockEmitter.LastEvent().(*apievents.AutoUpdateConfigCreate).Name) + mockEmitter.Reset() + + _, err = service.UpdateAutoUpdateConfig(ctx, &autoupdatev1pb.UpdateAutoUpdateConfigRequest{Config: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateConfigUpdateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateConfigUpdateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateConfig, mockEmitter.LastEvent().(*apievents.AutoUpdateConfigUpdate).Name) + mockEmitter.Reset() + + _, err = service.UpsertAutoUpdateConfig(ctx, &autoupdatev1pb.UpsertAutoUpdateConfigRequest{Config: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateConfigUpdateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateConfigUpdateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateConfig, mockEmitter.LastEvent().(*apievents.AutoUpdateConfigUpdate).Name) + mockEmitter.Reset() + + _, err = service.DeleteAutoUpdateConfig(ctx, &autoupdatev1pb.DeleteAutoUpdateConfigRequest{}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateConfigDeleteEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateConfigDeleteCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateConfig, mockEmitter.LastEvent().(*apievents.AutoUpdateConfigDelete).Name) + mockEmitter.Reset() +} + +func TestAutoUpdateVersionEvents(t *testing.T) { + rwVerbs := []string{types.VerbList, types.VerbCreate, types.VerbRead, types.VerbUpdate, types.VerbDelete} + mockEmitter := &eventstest.MockRecorderEmitter{} + service := newService(t, authz.AdminActionAuthMFAVerified, fakeChecker{allowedVerbs: rwVerbs}, mockEmitter) + ctx := context.Background() + + config, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }) + require.NoError(t, err) + + _, err = service.CreateAutoUpdateVersion(ctx, &autoupdatev1pb.CreateAutoUpdateVersionRequest{Version: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateVersionCreateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateVersionCreateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateVersion, mockEmitter.LastEvent().(*apievents.AutoUpdateVersionCreate).Name) + mockEmitter.Reset() + + _, err = service.UpdateAutoUpdateVersion(ctx, &autoupdatev1pb.UpdateAutoUpdateVersionRequest{Version: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateVersionUpdateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateVersionUpdateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateVersion, mockEmitter.LastEvent().(*apievents.AutoUpdateVersionUpdate).Name) + mockEmitter.Reset() + + _, err = service.UpsertAutoUpdateVersion(ctx, &autoupdatev1pb.UpsertAutoUpdateVersionRequest{Version: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateVersionUpdateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateVersionUpdateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateVersion, mockEmitter.LastEvent().(*apievents.AutoUpdateVersionUpdate).Name) + mockEmitter.Reset() + + _, err = service.DeleteAutoUpdateVersion(ctx, &autoupdatev1pb.DeleteAutoUpdateVersionRequest{}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateVersionDeleteEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateVersionDeleteCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateVersion, mockEmitter.LastEvent().(*apievents.AutoUpdateVersionDelete).Name) + mockEmitter.Reset() +} + type fakeChecker struct { allowedVerbs []string services.AccessChecker @@ -241,7 +337,7 @@ func (f fakeChecker) CheckAccessToRule(_ services.RuleContext, _ string, resourc return trace.AccessDenied("access denied to rule=%v/verb=%v", resource, verb) } -func newService(t *testing.T, authState authz.AdminActionAuthState, checker services.AccessChecker) *Service { +func newService(t *testing.T, authState authz.AdminActionAuthState, checker services.AccessChecker, emitter apievents.Emitter) *Service { t.Helper() bk, err := memory.New(memory.Config{}) @@ -250,10 +346,10 @@ func newService(t *testing.T, authState authz.AdminActionAuthState, checker serv storage, err := local.NewAutoUpdateService(bk) require.NoError(t, err) - return newServiceWithStorage(t, authState, checker, storage) + return newServiceWithStorage(t, authState, checker, storage, emitter) } -func newServiceWithStorage(t *testing.T, authState authz.AdminActionAuthState, checker services.AccessChecker, storage services.AutoUpdateService) *Service { +func newServiceWithStorage(t *testing.T, authState authz.AdminActionAuthState, checker services.AccessChecker, storage services.AutoUpdateService, emitter apievents.Emitter) *Service { t.Helper() authorizer := authz.AuthorizerFunc(func(ctx context.Context) (*authz.Context, error) { @@ -272,6 +368,7 @@ func newServiceWithStorage(t *testing.T, authState authz.AdminActionAuthState, c Authorizer: authorizer, Backend: storage, Cache: storage, + Emitter: emitter, }) require.NoError(t, err) return service diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index dadbf086c53a7..ab2e58090b250 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -6026,6 +6026,7 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { autoUpdateServiceServer, err := autoupdatev1.NewService(autoupdatev1.ServiceConfig{ Authorizer: cfg.Authorizer, + Emitter: cfg.Emitter, Backend: cfg.AuthServer.Services, Cache: cfg.AuthServer.Cache, }) diff --git a/lib/autoupdate/tools/utils.go b/lib/autoupdate/tools/utils.go index f937d228b5cd4..178e2818dd274 100644 --- a/lib/autoupdate/tools/utils.go +++ b/lib/autoupdate/tools/utils.go @@ -145,7 +145,7 @@ func teleportPackageURLs(baseURL, toolsVersion string) ([]packageURL, error) { m := modules.GetModules() var b strings.Builder b.WriteString(baseURL + "/teleport-") - if m.IsEnterpriseBuild() || m.IsBoringBinary() { + if m.BuildType() == modules.BuildEnterprise || m.IsBoringBinary() { b.WriteString("ent-") } b.WriteString("v" + toolsVersion + "-" + runtime.GOOS + "-" + runtime.GOARCH + "-") diff --git a/lib/events/api.go b/lib/events/api.go index 201e919e98456..a2e5673fc9d99 100644 --- a/lib/events/api.go +++ b/lib/events/api.go @@ -791,6 +791,20 @@ const ( CrownJewelUpdateEvent = "access_graph.crown_jewel.update" // CrownJewelDeleteEvent is emitted when a crown jewel resource is deleted. CrownJewelDeleteEvent = "access_graph.crown_jewel.delete" + + // AutoUpdateConfigCreateEvent is emitted when a AutoUpdateConfig resource is created. + AutoUpdateConfigCreateEvent = "auto_update_config.create" + // AutoUpdateConfigUpdateEvent is emitted when a AutoUpdateConfig resource is updated. + AutoUpdateConfigUpdateEvent = "auto_update_config.update" + // AutoUpdateConfigDeleteEvent is emitted when a AutoUpdateConfig resource is deleted. + AutoUpdateConfigDeleteEvent = "auto_update_config.delete" + + // AutoUpdateVersionCreateEvent is emitted when a AutoUpdateVersion resource is created. + AutoUpdateVersionCreateEvent = "auto_update_version.create" + // AutoUpdateVersionUpdateEvent is emitted when a AutoUpdateVersion resource is updated. + AutoUpdateVersionUpdateEvent = "auto_update_version.update" + // AutoUpdateVersionDeleteEvent is emitted when a AutoUpdateVersion resource is deleted. + AutoUpdateVersionDeleteEvent = "auto_update_version.delete" ) // Add an entry to eventsMap in lib/events/events_test.go when you add diff --git a/lib/events/codes.go b/lib/events/codes.go index e697f4b5742c7..969eb1a7daf8b 100644 --- a/lib/events/codes.go +++ b/lib/events/codes.go @@ -645,6 +645,20 @@ const ( // CrownJewelDeleteCode is the crown jewel delete event code. CrownJewelDeleteCode = "CJ003I" + // AutoUpdateConfigCreateCode is the auto update config create event code. + AutoUpdateConfigCreateCode = "AUC001I" + // AutoUpdateConfigUpdateCode is the auto update config update event code. + AutoUpdateConfigUpdateCode = "AUC002I" + // AutoUpdateConfigDeleteCode is the auto update config delete event code. + AutoUpdateConfigDeleteCode = "AUC003I" + + // AutoUpdateVersionCreateCode is the auto update version create event code. + AutoUpdateVersionCreateCode = "AUV001I" + // AutoUpdateVersionUpdateCode is the auto update version update event code. + AutoUpdateVersionUpdateCode = "AUV002I" + // AutoUpdateVersionDeleteCode is the auto update version delete event code. + AutoUpdateVersionDeleteCode = "AUV003I" + // UnknownCode is used when an event of unknown type is encountered. UnknownCode = apievents.UnknownCode ) diff --git a/lib/events/dynamic.go b/lib/events/dynamic.go index 1975d387ec0e0..f1dd33435344d 100644 --- a/lib/events/dynamic.go +++ b/lib/events/dynamic.go @@ -422,6 +422,20 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) { e = &events.CrownJewelUpdate{} case CrownJewelDeleteEvent: e = &events.CrownJewelDelete{} + + case AutoUpdateConfigCreateEvent: + e = &events.AutoUpdateConfigCreate{} + case AutoUpdateConfigUpdateEvent: + e = &events.AutoUpdateConfigUpdate{} + case AutoUpdateConfigDeleteEvent: + e = &events.AutoUpdateConfigDelete{} + + case AutoUpdateVersionCreateEvent: + e = &events.AutoUpdateVersionCreate{} + case AutoUpdateVersionUpdateEvent: + e = &events.AutoUpdateVersionUpdate{} + case AutoUpdateVersionDeleteEvent: + e = &events.AutoUpdateVersionDelete{} default: log.Errorf("Attempted to convert dynamic event of unknown type %q into protobuf event.", eventType) unknown := &events.Unknown{} diff --git a/lib/events/events_test.go b/lib/events/events_test.go index 0d0086f0cae96..273ea8dced941 100644 --- a/lib/events/events_test.go +++ b/lib/events/events_test.go @@ -225,6 +225,12 @@ var eventsMap = map[string]apievents.AuditEvent{ CrownJewelCreateEvent: &apievents.CrownJewelCreate{}, CrownJewelUpdateEvent: &apievents.CrownJewelUpdate{}, CrownJewelDeleteEvent: &apievents.CrownJewelDelete{}, + AutoUpdateConfigCreateEvent: &apievents.AutoUpdateConfigCreate{}, + AutoUpdateConfigUpdateEvent: &apievents.AutoUpdateConfigUpdate{}, + AutoUpdateConfigDeleteEvent: &apievents.AutoUpdateConfigDelete{}, + AutoUpdateVersionCreateEvent: &apievents.AutoUpdateVersionCreate{}, + AutoUpdateVersionUpdateEvent: &apievents.AutoUpdateVersionUpdate{}, + AutoUpdateVersionDeleteEvent: &apievents.AutoUpdateVersionDelete{}, } // TestJSON tests JSON marshal events diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index efb98f79a6d89..d63448f3860dd 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -49,21 +49,19 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/julienschmidt/httprouter" + "github.com/sashabaranov/go-openai" "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" oteltrace "go.opentelemetry.io/otel/trace" tracepb "go.opentelemetry.io/proto/otlp/trace/v1" "golang.org/x/crypto/ssh" "golang.org/x/mod/semver" + "golang.org/x/time/rate" "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/types/known/timestamppb" "github.com/gravitational/teleport" "github.com/gravitational/teleport/api" - "github.com/gravitational/teleport/api/types/autoupdate" apiclient "github.com/gravitational/teleport/api/client" - "github.com/sashabaranov/go-openai" - "golang.org/x/time/rate" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" @@ -72,6 +70,7 @@ import ( "github.com/gravitational/teleport/api/mfa" apitracing "github.com/gravitational/teleport/api/observability/tracing" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/autoupdate" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/installers" "github.com/gravitational/teleport/api/utils/keys" From fed28b2de607a22aaf3cc7a665dc9f7c43ff0155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Cie=C5=9Blak?= Date: Tue, 19 Nov 2024 16:43:55 +0100 Subject: [PATCH 7/9] Connect: Make sure tsh auto-updates are turned off * Add dir for code shared between Node.js processes * Connect: Make sure tsh auto-updates are turned off * Pass TELEPORT_TOOLS_VERSION=off to tsh vnet-daemon --- .../teleterm/src/mainProcess/mainProcess.ts | 5 ++++ web/packages/teleterm/src/node/README.md | 2 ++ .../teleterm/src/node/tshAutoupdate.ts | 27 +++++++++++++++++++ .../services/pty/ptyHost/buildPtyOptions.ts | 8 ++++++ 4 files changed, 42 insertions(+) create mode 100644 web/packages/teleterm/src/node/README.md create mode 100644 web/packages/teleterm/src/node/tshAutoupdate.ts diff --git a/web/packages/teleterm/src/mainProcess/mainProcess.ts b/web/packages/teleterm/src/mainProcess/mainProcess.ts index e88abc6069467..e0e8d08f3fdd1 100644 --- a/web/packages/teleterm/src/mainProcess/mainProcess.ts +++ b/web/packages/teleterm/src/mainProcess/mainProcess.ts @@ -53,6 +53,10 @@ import * as grpcCreds from 'teleterm/services/grpcCredentials'; import { createTshdClient, TshdClient } from 'teleterm/services/tshd'; import { loggingInterceptor } from 'teleterm/services/tshd/interceptors'; import { staticConfig } from 'teleterm/staticConfig'; +import { + TSH_AUTOUPDATE_ENV_VAR, + TSH_AUTOUPDATE_OFF, +} from 'teleterm/node/tshAutoupdate'; import { ConfigService, @@ -188,6 +192,7 @@ export default class MainProcess { env: { ...process.env, TELEPORT_HOME: homeDir, + [TSH_AUTOUPDATE_ENV_VAR]: TSH_AUTOUPDATE_OFF, }, } ); diff --git a/web/packages/teleterm/src/node/README.md b/web/packages/teleterm/src/node/README.md new file mode 100644 index 0000000000000..28ea74a4d8d5c --- /dev/null +++ b/web/packages/teleterm/src/node/README.md @@ -0,0 +1,2 @@ +Files in this directory are executed within a Node.js process, be it the main process or the shared +process. diff --git a/web/packages/teleterm/src/node/tshAutoupdate.ts b/web/packages/teleterm/src/node/tshAutoupdate.ts new file mode 100644 index 0000000000000..8ac6d73d9b8b2 --- /dev/null +++ b/web/packages/teleterm/src/node/tshAutoupdate.ts @@ -0,0 +1,27 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * An env var which controls whether tsh is going to download an up-to-date version of itself + * to ~/.tsh/bin and re-execute itself. In Connect, we always want it to be set to 'off', as Connect + * needs to use the bundled tsh where the version of tsh matches exactly the version of Connect. + * + * See RFD 144 for more details. + */ +export const TSH_AUTOUPDATE_ENV_VAR = 'TELEPORT_TOOLS_VERSION'; +export const TSH_AUTOUPDATE_OFF = 'off'; diff --git a/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts b/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts index 65f7845435975..671b8e612acfe 100644 --- a/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts +++ b/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts @@ -21,6 +21,10 @@ import path, { delimiter } from 'path'; import { RuntimeSettings } from 'teleterm/mainProcess/types'; import { PtyProcessOptions } from 'teleterm/sharedProcess/ptyHost'; import { assertUnreachable } from 'teleterm/ui/utils'; +import { + TSH_AUTOUPDATE_ENV_VAR, + TSH_AUTOUPDATE_OFF, +} from 'teleterm/node/tshAutoupdate'; import { PtyCommand, @@ -63,6 +67,9 @@ export async function buildPtyOptions( throw error; }) .then(({ shellEnv, creationStatus }) => { + // combinedEnv is going to be used as env by every command coming out of buildPtyOptions. Some + // commands might add extra variables, but they shouldn't remove any of the env vars that are + // added here. const combinedEnv = { ...process.env, ...shellEnv, @@ -71,6 +78,7 @@ export async function buildPtyOptions( TELEPORT_HOME: settings.tshd.homeDir, TELEPORT_CLUSTER: cmd.clusterName, TELEPORT_PROXY: cmd.proxyHost, + [TSH_AUTOUPDATE_ENV_VAR]: TSH_AUTOUPDATE_OFF, }; return { From d1e8566bc424ff54c3b20e793aadb4c186dc929a Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Tue, 19 Nov 2024 10:20:09 -0500 Subject: [PATCH 8/9] Disable client tools auto update disabled if there are no home dir (#49159) Move updater to general tools package --- integration/autoupdate/tools/updater/main.go | 1 - integration/autoupdate/tools/updater_test.go | 3 - lib/autoupdate/tools/updater.go | 20 +++- lib/client/api.go | 34 +----- tool/common/updater/client_tools.go | 112 +++++++++++++++++++ tool/tctl/common/tctl.go | 38 +------ tool/tsh/common/tsh.go | 78 ++----------- 7 files changed, 138 insertions(+), 148 deletions(-) create mode 100644 tool/common/updater/client_tools.go diff --git a/integration/autoupdate/tools/updater/main.go b/integration/autoupdate/tools/updater/main.go index 775c7ab7b2e9d..8a12dbbd1c9f7 100644 --- a/integration/autoupdate/tools/updater/main.go +++ b/integration/autoupdate/tools/updater/main.go @@ -43,7 +43,6 @@ func main() { ctx, _ = signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) updater := tools.NewUpdater( - tools.DefaultClientTools(), toolsDir, version, tools.WithBaseURL(baseURL), diff --git a/integration/autoupdate/tools/updater_test.go b/integration/autoupdate/tools/updater_test.go index 20660f72f06e2..6e617b3e3cdb1 100644 --- a/integration/autoupdate/tools/updater_test.go +++ b/integration/autoupdate/tools/updater_test.go @@ -49,7 +49,6 @@ func TestUpdate(t *testing.T) { // Fetch compiled test binary with updater logic and install to $TELEPORT_HOME. updater := tools.NewUpdater( - tools.DefaultClientTools(), toolsDir, testVersions[0], tools.WithBaseURL(baseURL), @@ -91,7 +90,6 @@ func TestParallelUpdate(t *testing.T) { // Initial fetch the updater binary un-archive and replace. updater := tools.NewUpdater( - tools.DefaultClientTools(), toolsDir, testVersions[0], tools.WithBaseURL(baseURL), @@ -165,7 +163,6 @@ func TestUpdateInterruptSignal(t *testing.T) { // Initial fetch the updater binary un-archive and replace. updater := tools.NewUpdater( - tools.DefaultClientTools(), toolsDir, testVersions[0], tools.WithBaseURL(baseURL), diff --git a/lib/autoupdate/tools/updater.go b/lib/autoupdate/tools/updater.go index 96352e34d9910..8bad07c395391 100644 --- a/lib/autoupdate/tools/updater.go +++ b/lib/autoupdate/tools/updater.go @@ -82,6 +82,13 @@ func WithClient(client *http.Client) Option { } } +// WithTools defines custom list of the tools has to be installed by updater. +func WithTools(tools []string) Option { + return func(u *Updater) { + u.tools = tools + } +} + // Updater is updater implementation for the client tools auto updates. type Updater struct { toolsDir string @@ -92,13 +99,14 @@ type Updater struct { client *http.Client } -// NewUpdater initializes the updater for client tools auto updates. We need to specify the list -// of tools (e.g., `tsh`, `tctl`) that should be updated, the tools directory path where we -// download, extract package archives with the new version, and replace symlinks (e.g., `$TELEPORT_HOME/bin`). -// The base URL of the CDN with Teleport packages and the `http.Client` can be customized via options. -func NewUpdater(tools []string, toolsDir string, localVersion string, options ...Option) *Updater { +// NewUpdater initializes the updater for client tools auto updates. We need to specify the tools directory +// path where we download, extract package archives with the new version, and replace symlinks +// (e.g., `$TELEPORT_HOME/bin`). +// The base URL of the CDN with Teleport packages, the `http.Client` and the list of tools (e.g., `tsh`, `tctl`) +// that must be updated can be customized via options. +func NewUpdater(toolsDir, localVersion string, options ...Option) *Updater { updater := &Updater{ - tools: tools, + tools: DefaultClientTools(), toolsDir: toolsDir, localVersion: localVersion, baseURL: baseURL, diff --git a/lib/client/api.go b/lib/client/api.go index 9eae61ca71277..abd65ca0dbea7 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -73,7 +73,6 @@ import ( "github.com/gravitational/teleport/lib/auth/touchid" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" "github.com/gravitational/teleport/lib/authz" - "github.com/gravitational/teleport/lib/autoupdate/tools" libmfa "github.com/gravitational/teleport/lib/client/mfa" "github.com/gravitational/teleport/lib/client/terminal" "github.com/gravitational/teleport/lib/defaults" @@ -93,7 +92,7 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/agentconn" "github.com/gravitational/teleport/lib/utils/proxy" - "github.com/gravitational/teleport/lib/utils/signal" + "github.com/gravitational/teleport/tool/common/updater" ) const ( @@ -670,38 +669,9 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error, return trace.Wrap(err) } - // The user has typed a command like `tsh ssh ...` without being logged in, - // if the running binary needs to be updated, update and re-exec. - // - // If needed, download the new version of {tsh, tctl} and re-exec. Make - // sure to exit this process with the same exit code as the child process. - // - toolsDir, err := tools.Dir() - if err != nil { + if err := updater.CheckAndUpdateRemote(ctx, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } - updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version) - toolsVersion, reExec, err := updater.CheckRemote(ctx, tc.WebProxyAddr, tc.InsecureSkipVerify) - if err != nil { - return trace.Wrap(err) - } - if reExec { - ctxUpdate, cancel := signal.GetSignalHandler().NotifyContext(context.Background()) - defer cancel() - // Download the version of client tools required by the cluster. - err := updater.UpdateWithLock(ctxUpdate, toolsVersion) - if err != nil && !errors.Is(err, context.Canceled) { - utils.FatalError(err) - } - // Re-execute client tools with the correct version of client tools. - code, err := updater.Exec() - if err != nil && !errors.Is(err, os.ErrNotExist) { - log.Debugf("Failed to re-exec client tool: %v.", err) - os.Exit(code) - } else if err == nil { - os.Exit(code) - } - } return fn() } diff --git a/tool/common/updater/client_tools.go b/tool/common/updater/client_tools.go new file mode 100644 index 0000000000000..6949664b35b7d --- /dev/null +++ b/tool/common/updater/client_tools.go @@ -0,0 +1,112 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package updater + +import ( + "context" + "errors" + "log/slog" + "os" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/lib/autoupdate/tools" + stacksignal "github.com/gravitational/teleport/lib/utils/signal" +) + +// CheckAndUpdateLocal verifies if the TELEPORT_TOOLS_VERSION environment variable +// is set and a version is defined (or disabled by setting it to "off"). The requested +// version is compared with the current client tools version. If they differ, the version +// package is downloaded, extracted to the client tools directory, and re-executed +// with the updated version. +// If $TELEPORT_HOME/bin contains downloaded client tools, it always re-executes +// using the version from the home directory. +func CheckAndUpdateLocal(ctx context.Context, currentVersion string) error { + toolsDir, err := tools.Dir() + if err != nil { + slog.WarnContext(ctx, "Client tools update is disabled", "error", err) + return nil + } + updater := tools.NewUpdater(toolsDir, currentVersion) + // At process startup, check if a version has already been downloaded to + // $TELEPORT_HOME/bin or if the user has set the TELEPORT_TOOLS_VERSION + // environment variable. If so, re-exec that version of client tools. + toolsVersion, reExec, err := updater.CheckLocal() + if err != nil { + return trace.Wrap(err) + } + if reExec { + return trace.Wrap(updateAndReExec(ctx, updater, toolsVersion)) + } + + return nil +} + +// CheckAndUpdateRemote verifies client tools version is set for update in cluster +// configuration by making the http request to `webapi/find` endpoint. The requested +// version is compared with the current client tools version. If they differ, the version +// package is downloaded, extracted to the client tools directory, and re-executed +// with the updated version. +// If $TELEPORT_HOME/bin contains downloaded client tools, it always re-executes +// using the version from the home directory. +func CheckAndUpdateRemote(ctx context.Context, currentVersion string, proxy string, insecure bool) error { + toolsDir, err := tools.Dir() + if err != nil { + slog.WarnContext(ctx, "Client tools update is disabled", "error", err) + return nil + } + updater := tools.NewUpdater(toolsDir, currentVersion) + // The user has typed a command like `tsh ssh ...` without being logged in, + // if the running binary needs to be updated, update and re-exec. + // + // If needed, download the new version of client tools and re-exec. Make + // sure to exit this process with the same exit code as the child process. + toolsVersion, reExec, err := updater.CheckRemote(ctx, proxy, insecure) + if err != nil { + return trace.Wrap(err) + } + if reExec { + return trace.Wrap(updateAndReExec(ctx, updater, toolsVersion)) + } + + return nil +} + +func updateAndReExec(ctx context.Context, updater *tools.Updater, toolsVersion string) error { + ctxUpdate, cancel := stacksignal.GetSignalHandler().NotifyContext(ctx) + defer cancel() + // Download the version of client tools required by the cluster. This + // is required if the user passed in the TELEPORT_TOOLS_VERSION + // explicitly. + err := updater.UpdateWithLock(ctxUpdate, toolsVersion) + if err != nil && !errors.Is(err, context.Canceled) { + return trace.Wrap(err) + } + + // Re-execute client tools with the correct version of client tools. + code, err := updater.Exec() + if err != nil && !errors.Is(err, os.ErrNotExist) { + slog.DebugContext(ctx, "Failed to re-exec client tool", "error", err) + os.Exit(code) + } else if err == nil { + os.Exit(code) + } + + return nil +} diff --git a/tool/tctl/common/tctl.go b/tool/tctl/common/tctl.go index b933995b14f29..ab2c0bd6e5e7a 100644 --- a/tool/tctl/common/tctl.go +++ b/tool/tctl/common/tctl.go @@ -43,7 +43,6 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/state" "github.com/gravitational/teleport/lib/auth/storage" - "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/identityfile" libmfa "github.com/gravitational/teleport/lib/client/mfa" @@ -54,8 +53,8 @@ import ( "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/hostid" - "github.com/gravitational/teleport/lib/utils/signal" "github.com/gravitational/teleport/tool/common" + "github.com/gravitational/teleport/tool/common/updater" ) const ( @@ -106,42 +105,11 @@ type CLICommand interface { // // distribution: name of the Teleport distribution func Run(ctx context.Context, commands []CLICommand) { - // The user has typed a command like `tsh ssh ...` without being logged in, - // if the running binary needs to be updated, update and re-exec. - // - // If needed, download the new version of {tsh, tctl} and re-exec. Make - // sure to exit this process with the same exit code as the child process. - // - toolsDir, err := tools.Dir() - if err != nil { - utils.FatalError(err) - } - updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version) - toolsVersion, reExec, err := updater.CheckLocal() - if err != nil { + if err := updater.CheckAndUpdateLocal(ctx, teleport.Version); err != nil { utils.FatalError(err) } - if reExec { - ctxUpdate, cancel := signal.GetSignalHandler().NotifyContext(ctx) - defer cancel() - // Download the version of client tools required by the cluster. This - // is required if the user passed in the TELEPORT_TOOLS_VERSION - // explicitly. - err := updater.UpdateWithLock(ctxUpdate, toolsVersion) - if err != nil && !errors.Is(err, context.Canceled) { - utils.FatalError(err) - } - // Re-execute client tools with the correct version of client tools. - code, err := updater.Exec() - if err != nil && !errors.Is(err, os.ErrNotExist) { - log.Debugf("Failed to re-exec client tool: %v.", err) - os.Exit(code) - } else if err == nil { - os.Exit(code) - } - } - err = TryRun(commands, os.Args[1:]) + err := TryRun(commands, os.Args[1:]) if err != nil { var exitError *common.ExitCodeError if errors.As(err, &exitError) { diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index f864351d8bdff..157a71c5145c8 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -73,7 +73,6 @@ import ( "github.com/gravitational/teleport/lib/asciitable" "github.com/gravitational/teleport/lib/auth/authclient" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" - "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/benchmark" benchmarkdb "github.com/gravitational/teleport/lib/benchmark/db" "github.com/gravitational/teleport/lib/client" @@ -98,6 +97,7 @@ import ( "github.com/gravitational/teleport/tool/common" "github.com/gravitational/teleport/tool/common/fido2" "github.com/gravitational/teleport/tool/common/touchid" + "github.com/gravitational/teleport/tool/common/updater" "github.com/gravitational/teleport/tool/common/webauthnwin" ) @@ -704,37 +704,9 @@ func initLogger(cf *CLIConf) { // // DO NOT RUN TESTS that call Run() in parallel (unless you taken precautions). func Run(ctx context.Context, args []string, opts ...CliOption) error { - // At process startup, check if a version has already been downloaded to - // $TELEPORT_HOME/bin or if the user has set the TELEPORT_TOOLS_VERSION - // environment variable. If so, re-exec that version of {tsh, tctl}. - toolsDir, err := tools.Dir() - if err != nil { - return trace.Wrap(err) - } - updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version) - toolsVersion, reExec, err := updater.CheckLocal() - if err != nil { + if err := updater.CheckAndUpdateLocal(ctx, teleport.Version); err != nil { return trace.Wrap(err) } - if reExec { - ctxUpdate, cancel := stacksignal.GetSignalHandler().NotifyContext(ctx) - defer cancel() - // Download the version of client tools required by the cluster. This - // is required if the user passed in the TELEPORT_TOOLS_VERSION - // explicitly. - err := updater.UpdateWithLock(ctxUpdate, toolsVersion) - if err != nil && !errors.Is(err, context.Canceled) { - return trace.Wrap(err) - } - // Re-execute client tools with the correct version of client tools. - code, err := updater.Exec() - if err != nil && !errors.Is(err, os.ErrNotExist) { - log.Debugf("Failed to re-exec client tool: %v.", err) - os.Exit(code) - } else if err == nil { - os.Exit(code) - } - } cf := CLIConf{ Context: ctx, @@ -1260,6 +1232,7 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { bench.Hidden() } + var err error cf.executablePath, err = os.Executable() if err != nil { return trace.Wrap(err) @@ -1882,7 +1855,7 @@ func onLogin(cf *CLIConf) error { // The user is not logged in and has typed in `tsh --proxy=... login`, if // the running binary needs to be updated, update and re-exec. if profile == nil { - if err := updateAndRun(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := updater.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } } @@ -1900,7 +1873,7 @@ func onLogin(cf *CLIConf) error { // The user has typed `tsh login`, if the running binary needs to // be updated, update and re-exec. - if err := updateAndRun(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := updater.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } @@ -1920,7 +1893,7 @@ func onLogin(cf *CLIConf) error { // The user has typed `tsh login`, if the running binary needs to // be updated, update and re-exec. - if err := updateAndRun(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := updater.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } @@ -1996,7 +1969,7 @@ func onLogin(cf *CLIConf) error { default: // The user is logged in and has typed in `tsh --proxy=... login`, if // the running binary needs to be updated, update and re-exec. - if err := updateAndRun(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := updater.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } } @@ -5527,43 +5500,6 @@ const ( "https://goteleport.com/docs/access-controls/guides/headless/#troubleshooting" ) -func updateAndRun(ctx context.Context, proxy string, insecure bool) error { - // The user has typed a command like `tsh ssh ...` without being logged in, - // if the running binary needs to be updated, update and re-exec. - // - // If needed, download the new version of {tsh, tctl} and re-exec. Make - // sure to exit this process with the same exit code as the child process. - // - toolsDir, err := tools.Dir() - if err != nil { - return trace.Wrap(err) - } - updater := tools.NewUpdater(tools.DefaultClientTools(), toolsDir, teleport.Version) - toolsVersion, reExec, err := updater.CheckRemote(ctx, proxy, insecure) - if err != nil { - return trace.Wrap(err) - } - if reExec { - ctxUpdate, cancel := stacksignal.GetSignalHandler().NotifyContext(context.Background()) - defer cancel() - // Download the version of client tools required by the cluster. - err := updater.UpdateWithLock(ctxUpdate, toolsVersion) - if err != nil && !errors.Is(err, context.Canceled) { - return trace.Wrap(err) - } - // Re-execute client tools with the correct version of client tools. - code, err := updater.Exec() - if err != nil && !errors.Is(err, os.ErrNotExist) { - log.Debugf("Failed to re-exec client tool: %v.", err) - os.Exit(code) - } else if err == nil { - os.Exit(code) - } - } - - return nil -} - // Lock the process memory to prevent rsa keys and certificates in memory from being exposed in a swap. func tryLockMemory(cf *CLIConf) error { if cf.MlockMode == mlockModeAuto { From fcb80373125a4d5adcf2d09d349a45c349a303b1 Mon Sep 17 00:00:00 2001 From: Vadym Popov Date: Wed, 20 Nov 2024 04:59:56 -0500 Subject: [PATCH 9/9] Move client auto update helper to lib package (#49247) --- .../autoupdate/tools/helper.go | 13 ++++++------- lib/client/api.go | 4 ++-- tool/tctl/common/tctl.go | 4 ++-- tool/tsh/common/tsh.go | 12 ++++++------ 4 files changed, 16 insertions(+), 17 deletions(-) rename tool/common/updater/client_tools.go => lib/autoupdate/tools/helper.go (91%) diff --git a/tool/common/updater/client_tools.go b/lib/autoupdate/tools/helper.go similarity index 91% rename from tool/common/updater/client_tools.go rename to lib/autoupdate/tools/helper.go index 6949664b35b7d..79069c3d3c90b 100644 --- a/tool/common/updater/client_tools.go +++ b/lib/autoupdate/tools/helper.go @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package updater +package tools import ( "context" @@ -26,7 +26,6 @@ import ( "github.com/gravitational/trace" - "github.com/gravitational/teleport/lib/autoupdate/tools" stacksignal "github.com/gravitational/teleport/lib/utils/signal" ) @@ -38,12 +37,12 @@ import ( // If $TELEPORT_HOME/bin contains downloaded client tools, it always re-executes // using the version from the home directory. func CheckAndUpdateLocal(ctx context.Context, currentVersion string) error { - toolsDir, err := tools.Dir() + toolsDir, err := Dir() if err != nil { slog.WarnContext(ctx, "Client tools update is disabled", "error", err) return nil } - updater := tools.NewUpdater(toolsDir, currentVersion) + updater := NewUpdater(toolsDir, currentVersion) // At process startup, check if a version has already been downloaded to // $TELEPORT_HOME/bin or if the user has set the TELEPORT_TOOLS_VERSION // environment variable. If so, re-exec that version of client tools. @@ -66,12 +65,12 @@ func CheckAndUpdateLocal(ctx context.Context, currentVersion string) error { // If $TELEPORT_HOME/bin contains downloaded client tools, it always re-executes // using the version from the home directory. func CheckAndUpdateRemote(ctx context.Context, currentVersion string, proxy string, insecure bool) error { - toolsDir, err := tools.Dir() + toolsDir, err := Dir() if err != nil { slog.WarnContext(ctx, "Client tools update is disabled", "error", err) return nil } - updater := tools.NewUpdater(toolsDir, currentVersion) + updater := NewUpdater(toolsDir, currentVersion) // The user has typed a command like `tsh ssh ...` without being logged in, // if the running binary needs to be updated, update and re-exec. // @@ -88,7 +87,7 @@ func CheckAndUpdateRemote(ctx context.Context, currentVersion string, proxy stri return nil } -func updateAndReExec(ctx context.Context, updater *tools.Updater, toolsVersion string) error { +func updateAndReExec(ctx context.Context, updater *Updater, toolsVersion string) error { ctxUpdate, cancel := stacksignal.GetSignalHandler().NotifyContext(ctx) defer cancel() // Download the version of client tools required by the cluster. This diff --git a/lib/client/api.go b/lib/client/api.go index abd65ca0dbea7..abece10e4eab1 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -73,6 +73,7 @@ import ( "github.com/gravitational/teleport/lib/auth/touchid" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/autoupdate/tools" libmfa "github.com/gravitational/teleport/lib/client/mfa" "github.com/gravitational/teleport/lib/client/terminal" "github.com/gravitational/teleport/lib/defaults" @@ -92,7 +93,6 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/agentconn" "github.com/gravitational/teleport/lib/utils/proxy" - "github.com/gravitational/teleport/tool/common/updater" ) const ( @@ -669,7 +669,7 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error, return trace.Wrap(err) } - if err := updater.CheckAndUpdateRemote(ctx, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := tools.CheckAndUpdateRemote(ctx, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } diff --git a/tool/tctl/common/tctl.go b/tool/tctl/common/tctl.go index ab2c0bd6e5e7a..6644bbbbdcfdc 100644 --- a/tool/tctl/common/tctl.go +++ b/tool/tctl/common/tctl.go @@ -43,6 +43,7 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/state" "github.com/gravitational/teleport/lib/auth/storage" + "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/identityfile" libmfa "github.com/gravitational/teleport/lib/client/mfa" @@ -54,7 +55,6 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/hostid" "github.com/gravitational/teleport/tool/common" - "github.com/gravitational/teleport/tool/common/updater" ) const ( @@ -105,7 +105,7 @@ type CLICommand interface { // // distribution: name of the Teleport distribution func Run(ctx context.Context, commands []CLICommand) { - if err := updater.CheckAndUpdateLocal(ctx, teleport.Version); err != nil { + if err := tools.CheckAndUpdateLocal(ctx, teleport.Version); err != nil { utils.FatalError(err) } diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index 157a71c5145c8..8cd97e95d6f73 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -73,6 +73,7 @@ import ( "github.com/gravitational/teleport/lib/asciitable" "github.com/gravitational/teleport/lib/auth/authclient" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/benchmark" benchmarkdb "github.com/gravitational/teleport/lib/benchmark/db" "github.com/gravitational/teleport/lib/client" @@ -97,7 +98,6 @@ import ( "github.com/gravitational/teleport/tool/common" "github.com/gravitational/teleport/tool/common/fido2" "github.com/gravitational/teleport/tool/common/touchid" - "github.com/gravitational/teleport/tool/common/updater" "github.com/gravitational/teleport/tool/common/webauthnwin" ) @@ -704,7 +704,7 @@ func initLogger(cf *CLIConf) { // // DO NOT RUN TESTS that call Run() in parallel (unless you taken precautions). func Run(ctx context.Context, args []string, opts ...CliOption) error { - if err := updater.CheckAndUpdateLocal(ctx, teleport.Version); err != nil { + if err := tools.CheckAndUpdateLocal(ctx, teleport.Version); err != nil { return trace.Wrap(err) } @@ -1855,7 +1855,7 @@ func onLogin(cf *CLIConf) error { // The user is not logged in and has typed in `tsh --proxy=... login`, if // the running binary needs to be updated, update and re-exec. if profile == nil { - if err := updater.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := tools.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } } @@ -1873,7 +1873,7 @@ func onLogin(cf *CLIConf) error { // The user has typed `tsh login`, if the running binary needs to // be updated, update and re-exec. - if err := updater.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := tools.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } @@ -1893,7 +1893,7 @@ func onLogin(cf *CLIConf) error { // The user has typed `tsh login`, if the running binary needs to // be updated, update and re-exec. - if err := updater.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := tools.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } @@ -1969,7 +1969,7 @@ func onLogin(cf *CLIConf) error { default: // The user is logged in and has typed in `tsh --proxy=... login`, if // the running binary needs to be updated, update and re-exec. - if err := updater.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { + if err := tools.CheckAndUpdateRemote(cf.Context, teleport.Version, tc.WebProxyAddr, tc.InsecureSkipVerify); err != nil { return trace.Wrap(err) } }