From 318865fde4b211da96b8468a8fa061daebdf7763 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Tue, 7 Apr 2020 23:28:44 -0400 Subject: [PATCH 01/18] network/dns: plumb dns options through to task drivers --- api/resources.go | 7 + client/allocrunner/taskrunner/task_runner.go | 11 + command/agent/job_endpoint.go | 8 + drivers/docker/driver.go | 16 +- jobspec/parse_network.go | 61 +- jobspec/parse_test.go | 4 + jobspec/test-fixtures/tg-network.hcl | 5 + nomad/structs/network.go | 1 + nomad/structs/structs.go | 21 +- plugins/drivers/driver.go | 30 + plugins/drivers/proto/driver.pb.go | 564 +++++++++++-------- plugins/drivers/proto/driver.proto | 9 + plugins/drivers/utils.go | 26 + 13 files changed, 495 insertions(+), 268 deletions(-) diff --git a/api/resources.go b/api/resources.go index cbd24c4bf81..fe5fb521538 100644 --- a/api/resources.go +++ b/api/resources.go @@ -89,6 +89,12 @@ type Port struct { To int `mapstructure:"to"` } +type DNSConfig struct { + Servers []string `mapstructure:"servers"` + Searches []string `mapstructure:"searches"` + Options []string `mapstructure:"options"` +} + // NetworkResource is used to describe required network // resources of a given task. type NetworkResource struct { @@ -97,6 +103,7 @@ type NetworkResource struct { CIDR string IP string MBits *int + DNS *DNSConfig ReservedPorts []Port DynamicPorts []Port } diff --git a/client/allocrunner/taskrunner/task_runner.go b/client/allocrunner/taskrunner/task_runner.go index 43712f14094..af92de49832 100644 --- a/client/allocrunner/taskrunner/task_runner.go +++ b/client/allocrunner/taskrunner/task_runner.go @@ -926,12 +926,22 @@ func (tr *TaskRunner) persistLocalState() error { func (tr *TaskRunner) buildTaskConfig() *drivers.TaskConfig { task := tr.Task() alloc := tr.Alloc() + allocDNS := alloc.AllocatedResources.Shared.Networks[0].DNS invocationid := uuid.Generate()[:8] taskResources := tr.taskResources env := tr.envBuilder.Build() tr.networkIsolationLock.Lock() defer tr.networkIsolationLock.Unlock() + var dns *drivers.DNSConfig + if allocDNS != nil { + dns = &drivers.DNSConfig{ + Servers: allocDNS.Servers, + Searches: allocDNS.Searches, + Options: allocDNS.Options, + } + } + return &drivers.TaskConfig{ ID: fmt.Sprintf("%s/%s/%s", alloc.ID, task.Name, invocationid), Name: task.Name, @@ -955,6 +965,7 @@ func (tr *TaskRunner) buildTaskConfig() *drivers.TaskConfig { StderrPath: tr.logmonHookConfig.stderrFifo, AllocID: tr.allocID, NetworkIsolation: tr.networkIsolationSpec, + DNS: dns, } } diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index 09bac74cc2e..9a20cd7244b 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -1095,6 +1095,14 @@ func ApiNetworkResourceToStructs(in []*api.NetworkResource) []*structs.NetworkRe MBits: *nw.MBits, } + if nw.DNS != nil { + out[i].DNS = &structs.DNSConfig{ + Servers: nw.DNS.Servers, + Searches: nw.DNS.Searches, + Options: nw.DNS.Options, + } + } + if l := len(nw.DynamicPorts); l != 0 { out[i].DynamicPorts = make([]structs.Port, l) for j, dp := range nw.DynamicPorts { diff --git a/drivers/docker/driver.go b/drivers/docker/driver.go index 5880d976d2e..b0bc5221dfc 100644 --- a/drivers/docker/driver.go +++ b/drivers/docker/driver.go @@ -857,6 +857,19 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T hostConfig.ShmSize = driverConfig.ShmSize } + // setup Nomad DNS options, these are overriden by docker driver specific options + if task.DNS != nil { + hostConfig.DNS = task.DNS.Servers + hostConfig.DNSSearch = task.DNS.Searches + hostConfig.DNSOptions = task.DNS.Options + } + + if len(driverConfig.DNSSearchDomains) > 0 { + hostConfig.DNSSearch = driverConfig.DNSSearchDomains + } + if len(driverConfig.DNSOptions) > 0 { + hostConfig.DNSOptions = driverConfig.DNSOptions + } // set DNS servers for _, ip := range driverConfig.DNSServers { if net.ParseIP(ip) != nil { @@ -920,9 +933,6 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T hostConfig.Mounts = append(hostConfig.Mounts, hm) } - // set DNS search domains and extra hosts - hostConfig.DNSSearch = driverConfig.DNSSearchDomains - hostConfig.DNSOptions = driverConfig.DNSOptions hostConfig.ExtraHosts = driverConfig.ExtraHosts hostConfig.IpcMode = driverConfig.IPCMode diff --git a/jobspec/parse_network.go b/jobspec/parse_network.go index 6cd2ca6cbaf..e72719309f2 100644 --- a/jobspec/parse_network.go +++ b/jobspec/parse_network.go @@ -22,6 +22,7 @@ func ParseNetwork(o *ast.ObjectList) (*api.NetworkResource, error) { valid := []string{ "mode", "mbits", + "dns", "port", } if err := helper.CheckHCLKeys(o.Items[0].Val, valid); err != nil { @@ -33,6 +34,8 @@ func ParseNetwork(o *ast.ObjectList) (*api.NetworkResource, error) { if err := hcl.DecodeObject(&m, o.Items[0].Val); err != nil { return nil, err } + + delete(m, "dns") if err := mapstructure.WeakDecode(m, &r); err != nil { return nil, err } @@ -47,26 +50,40 @@ func ParseNetwork(o *ast.ObjectList) (*api.NetworkResource, error) { return nil, multierror.Prefix(err, "network, ports ->") } + // Filter dns + if dns := networkObj.Filter("dns"); len(dns.Items) > 0 { + if len(dns.Items) > 1 { + return nil, multierror.Prefix(fmt.Errorf("cannot have more than 1 dns stanza"), "network ->") + } + + d, err := parseDNS(dns.Items[0]) + if err != nil { + return nil, multierror.Prefix(err, "network ->") + } + + r.DNS = d + } + return &r, nil } func parsePorts(networkObj *ast.ObjectList, nw *api.NetworkResource) error { - // Check for invalid keys - valid := []string{ - "mbits", - "port", - "mode", - } - if err := helper.CheckHCLKeys(networkObj, valid); err != nil { - return err - } - portsObjList := networkObj.Filter("port") knownPortLabels := make(map[string]bool) for _, port := range portsObjList.Items { if len(port.Keys) == 0 { return fmt.Errorf("ports must be named") } + + // check for invalid keys + valid := []string{ + "static", + "to", + } + if err := helper.CheckHCLKeys(port.Val, valid); err != nil { + return err + } + label := port.Keys[0].Token.Value().(string) if !reDynamicPorts.MatchString(label) { return errPortLabel @@ -93,3 +110,27 @@ func parsePorts(networkObj *ast.ObjectList, nw *api.NetworkResource) error { } return nil } + +func parseDNS(dns *ast.ObjectItem) (*api.DNSConfig, error) { + valid := []string{ + "servers", + "searches", + "options", + } + + if err := helper.CheckHCLKeys(dns.Val, valid); err != nil { + return nil, multierror.Prefix(err, "dns ->") + } + + var dnsCfg api.DNSConfig + var m map[string]interface{} + if err := hcl.DecodeObject(&m, dns.Val); err != nil { + return nil, err + } + + if err := mapstructure.WeakDecode(m, &dnsCfg); err != nil { + return nil, err + } + + return &dnsCfg, nil +} diff --git a/jobspec/parse_test.go b/jobspec/parse_test.go index c445a681829..efb5d001124 100644 --- a/jobspec/parse_test.go +++ b/jobspec/parse_test.go @@ -1017,6 +1017,10 @@ func TestParse(t *testing.T) { To: 8080, }, }, + DNS: &api.DNSConfig{ + Servers: []string{"8.8.8.8"}, + Options: []string{"ndots:2", "edns0"}, + }, }, }, Services: []*api.Service{ diff --git a/jobspec/test-fixtures/tg-network.hcl b/jobspec/test-fixtures/tg-network.hcl index 538f49f3acf..0aca76bf040 100644 --- a/jobspec/test-fixtures/tg-network.hcl +++ b/jobspec/test-fixtures/tg-network.hcl @@ -12,6 +12,11 @@ job "foo" { static = 80 to = 8080 } + + dns { + servers = ["8.8.8.8"], + options = ["ndots:2", "edns0"], + } } service { diff --git a/nomad/structs/network.go b/nomad/structs/network.go index e37158cf184..9ea7e462103 100644 --- a/nomad/structs/network.go +++ b/nomad/structs/network.go @@ -290,6 +290,7 @@ func (idx *NetworkIndex) AssignNetwork(ask *NetworkResource) (out *NetworkResour Device: n.Device, IP: ipStr, MBits: ask.MBits, + DNS: ask.DNS, ReservedPorts: ask.ReservedPorts, DynamicPorts: ask.DynamicPorts, } diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 453431d615d..29adbe3e355 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -2208,16 +2208,23 @@ type Port struct { To int } +type DNSConfig struct { + Servers []string + Searches []string + Options []string +} + // NetworkResource is used to represent available network // resources type NetworkResource struct { - Mode string // Mode of the network - Device string // Name of the device - CIDR string // CIDR block of addresses - IP string // Host IP address - MBits int // Throughput - ReservedPorts []Port // Host Reserved ports - DynamicPorts []Port // Host Dynamically assigned ports + Mode string // Mode of the network + Device string // Name of the device + CIDR string // CIDR block of addresses + IP string // Host IP address + MBits int // Throughput + DNS *DNSConfig // DNS Configuration + ReservedPorts []Port // Host Reserved ports + DynamicPorts []Port // Host Dynamically assigned ports } func (nr *NetworkResource) Equals(other *NetworkResource) bool { diff --git a/plugins/drivers/driver.go b/plugins/drivers/driver.go index 835ec1c31d2..05326486c6d 100644 --- a/plugins/drivers/driver.go +++ b/plugins/drivers/driver.go @@ -202,6 +202,34 @@ type TerminalSize struct { Width int } +type DNSConfig struct { + Servers []string + Searches []string + Options []string +} + +func (c *DNSConfig) Copy() *DNSConfig { + if c == nil { + return nil + } + + cfg := new(DNSConfig) + if len(c.Servers) > 0 { + cfg.Servers = make([]string, len(c.Servers)) + copy(cfg.Servers, c.Servers) + } + if len(c.Searches) > 0 { + cfg.Searches = make([]string, len(c.Searches)) + copy(cfg.Searches, c.Searches) + } + if len(c.Options) > 0 { + cfg.Options = make([]string, len(c.Options)) + copy(cfg.Options, c.Options) + } + + return cfg +} + type TaskConfig struct { ID string JobName string @@ -219,6 +247,7 @@ type TaskConfig struct { StderrPath string AllocID string NetworkIsolation *NetworkIsolationSpec + DNS *DNSConfig } func (tc *TaskConfig) Copy() *TaskConfig { @@ -230,6 +259,7 @@ func (tc *TaskConfig) Copy() *TaskConfig { c.Env = helper.CopyMapStringString(c.Env) c.DeviceEnv = helper.CopyMapStringString(c.DeviceEnv) c.Resources = tc.Resources.Copy() + c.DNS = tc.DNS.Copy() if c.Devices != nil { dc := make([]*DeviceConfig, len(c.Devices)) diff --git a/plugins/drivers/proto/driver.pb.go b/plugins/drivers/proto/driver.pb.go index 7b521867c4d..d37160a8db7 100644 --- a/plugins/drivers/proto/driver.pb.go +++ b/plugins/drivers/proto/driver.pb.go @@ -205,7 +205,7 @@ func (x CPUUsage_Fields) String() string { } func (CPUUsage_Fields) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{51, 0} + return fileDescriptor_4a8f45747846a74d, []int{52, 0} } type MemoryUsage_Fields int32 @@ -245,7 +245,7 @@ func (x MemoryUsage_Fields) String() string { } func (MemoryUsage_Fields) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{52, 0} + return fileDescriptor_4a8f45747846a74d, []int{53, 0} } type TaskConfigSchemaRequest struct { @@ -1927,6 +1927,61 @@ func (m *NetworkIsolationSpec) GetLabels() map[string]string { return nil } +type DNSConfig struct { + Servers []string `protobuf:"bytes,1,rep,name=servers,proto3" json:"servers,omitempty"` + Searches []string `protobuf:"bytes,2,rep,name=searches,proto3" json:"searches,omitempty"` + Options []string `protobuf:"bytes,3,rep,name=options,proto3" json:"options,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DNSConfig) Reset() { *m = DNSConfig{} } +func (m *DNSConfig) String() string { return proto.CompactTextString(m) } +func (*DNSConfig) ProtoMessage() {} +func (*DNSConfig) Descriptor() ([]byte, []int) { + return fileDescriptor_4a8f45747846a74d, []int{34} +} + +func (m *DNSConfig) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DNSConfig.Unmarshal(m, b) +} +func (m *DNSConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DNSConfig.Marshal(b, m, deterministic) +} +func (m *DNSConfig) XXX_Merge(src proto.Message) { + xxx_messageInfo_DNSConfig.Merge(m, src) +} +func (m *DNSConfig) XXX_Size() int { + return xxx_messageInfo_DNSConfig.Size(m) +} +func (m *DNSConfig) XXX_DiscardUnknown() { + xxx_messageInfo_DNSConfig.DiscardUnknown(m) +} + +var xxx_messageInfo_DNSConfig proto.InternalMessageInfo + +func (m *DNSConfig) GetServers() []string { + if m != nil { + return m.Servers + } + return nil +} + +func (m *DNSConfig) GetSearches() []string { + if m != nil { + return m.Searches + } + return nil +} + +func (m *DNSConfig) GetOptions() []string { + if m != nil { + return m.Options + } + return nil +} + type TaskConfig struct { // Id of the task, recommended to the globally unique, must be unique to the driver. Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` @@ -1966,16 +2021,18 @@ type TaskConfig struct { // NetworkIsolationSpec specifies the configuration for the network namespace // to use for the task. *Only supported on Linux NetworkIsolationSpec *NetworkIsolationSpec `protobuf:"bytes,16,opt,name=network_isolation_spec,json=networkIsolationSpec,proto3" json:"network_isolation_spec,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // DNSConfig is the configuration for task DNS resolvers and other options + Dns *DNSConfig `protobuf:"bytes,17,opt,name=dns,proto3" json:"dns,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *TaskConfig) Reset() { *m = TaskConfig{} } func (m *TaskConfig) String() string { return proto.CompactTextString(m) } func (*TaskConfig) ProtoMessage() {} func (*TaskConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{34} + return fileDescriptor_4a8f45747846a74d, []int{35} } func (m *TaskConfig) XXX_Unmarshal(b []byte) error { @@ -2108,6 +2165,13 @@ func (m *TaskConfig) GetNetworkIsolationSpec() *NetworkIsolationSpec { return nil } +func (m *TaskConfig) GetDns() *DNSConfig { + if m != nil { + return m.Dns + } + return nil +} + type Resources struct { // AllocatedResources are the resources set for the task AllocatedResources *AllocatedTaskResources `protobuf:"bytes,1,opt,name=allocated_resources,json=allocatedResources,proto3" json:"allocated_resources,omitempty"` @@ -2122,7 +2186,7 @@ func (m *Resources) Reset() { *m = Resources{} } func (m *Resources) String() string { return proto.CompactTextString(m) } func (*Resources) ProtoMessage() {} func (*Resources) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{35} + return fileDescriptor_4a8f45747846a74d, []int{36} } func (m *Resources) XXX_Unmarshal(b []byte) error { @@ -2170,7 +2234,7 @@ func (m *AllocatedTaskResources) Reset() { *m = AllocatedTaskResources{} func (m *AllocatedTaskResources) String() string { return proto.CompactTextString(m) } func (*AllocatedTaskResources) ProtoMessage() {} func (*AllocatedTaskResources) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{36} + return fileDescriptor_4a8f45747846a74d, []int{37} } func (m *AllocatedTaskResources) XXX_Unmarshal(b []byte) error { @@ -2223,7 +2287,7 @@ func (m *AllocatedCpuResources) Reset() { *m = AllocatedCpuResources{} } func (m *AllocatedCpuResources) String() string { return proto.CompactTextString(m) } func (*AllocatedCpuResources) ProtoMessage() {} func (*AllocatedCpuResources) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{37} + return fileDescriptor_4a8f45747846a74d, []int{38} } func (m *AllocatedCpuResources) XXX_Unmarshal(b []byte) error { @@ -2262,7 +2326,7 @@ func (m *AllocatedMemoryResources) Reset() { *m = AllocatedMemoryResourc func (m *AllocatedMemoryResources) String() string { return proto.CompactTextString(m) } func (*AllocatedMemoryResources) ProtoMessage() {} func (*AllocatedMemoryResources) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{38} + return fileDescriptor_4a8f45747846a74d, []int{39} } func (m *AllocatedMemoryResources) XXX_Unmarshal(b []byte) error { @@ -2306,7 +2370,7 @@ func (m *NetworkResource) Reset() { *m = NetworkResource{} } func (m *NetworkResource) String() string { return proto.CompactTextString(m) } func (*NetworkResource) ProtoMessage() {} func (*NetworkResource) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{39} + return fileDescriptor_4a8f45747846a74d, []int{40} } func (m *NetworkResource) XXX_Unmarshal(b []byte) error { @@ -2381,7 +2445,7 @@ func (m *NetworkPort) Reset() { *m = NetworkPort{} } func (m *NetworkPort) String() string { return proto.CompactTextString(m) } func (*NetworkPort) ProtoMessage() {} func (*NetworkPort) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{40} + return fileDescriptor_4a8f45747846a74d, []int{41} } func (m *NetworkPort) XXX_Unmarshal(b []byte) error { @@ -2432,7 +2496,7 @@ type LinuxResources struct { // CpusetMems constrains the allowed set of memory nodes. Default: "" (not specified) CpusetMems string `protobuf:"bytes,7,opt,name=cpuset_mems,json=cpusetMems,proto3" json:"cpuset_mems,omitempty"` // PercentTicks is a compatibility option for docker and should not be used - PercentTicks float64 `protobuf:"fixed64,8,opt,name=PercentTicks,proto3" json:"PercentTicks,omitempty"` + PercentTicks float64 `protobuf:"fixed64,8,opt,name=PercentTicks,json=percentTicks,proto3" json:"PercentTicks,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2442,7 +2506,7 @@ func (m *LinuxResources) Reset() { *m = LinuxResources{} } func (m *LinuxResources) String() string { return proto.CompactTextString(m) } func (*LinuxResources) ProtoMessage() {} func (*LinuxResources) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{41} + return fileDescriptor_4a8f45747846a74d, []int{42} } func (m *LinuxResources) XXX_Unmarshal(b []byte) error { @@ -2535,7 +2599,7 @@ func (m *Mount) Reset() { *m = Mount{} } func (m *Mount) String() string { return proto.CompactTextString(m) } func (*Mount) ProtoMessage() {} func (*Mount) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{42} + return fileDescriptor_4a8f45747846a74d, []int{43} } func (m *Mount) XXX_Unmarshal(b []byte) error { @@ -2599,7 +2663,7 @@ func (m *Device) Reset() { *m = Device{} } func (m *Device) String() string { return proto.CompactTextString(m) } func (*Device) ProtoMessage() {} func (*Device) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{43} + return fileDescriptor_4a8f45747846a74d, []int{44} } func (m *Device) XXX_Unmarshal(b []byte) error { @@ -2661,7 +2725,7 @@ func (m *TaskHandle) Reset() { *m = TaskHandle{} } func (m *TaskHandle) String() string { return proto.CompactTextString(m) } func (*TaskHandle) ProtoMessage() {} func (*TaskHandle) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{44} + return fileDescriptor_4a8f45747846a74d, []int{45} } func (m *TaskHandle) XXX_Unmarshal(b []byte) error { @@ -2729,7 +2793,7 @@ func (m *NetworkOverride) Reset() { *m = NetworkOverride{} } func (m *NetworkOverride) String() string { return proto.CompactTextString(m) } func (*NetworkOverride) ProtoMessage() {} func (*NetworkOverride) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{45} + return fileDescriptor_4a8f45747846a74d, []int{46} } func (m *NetworkOverride) XXX_Unmarshal(b []byte) error { @@ -2788,7 +2852,7 @@ func (m *ExitResult) Reset() { *m = ExitResult{} } func (m *ExitResult) String() string { return proto.CompactTextString(m) } func (*ExitResult) ProtoMessage() {} func (*ExitResult) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{46} + return fileDescriptor_4a8f45747846a74d, []int{47} } func (m *ExitResult) XXX_Unmarshal(b []byte) error { @@ -2852,7 +2916,7 @@ func (m *TaskStatus) Reset() { *m = TaskStatus{} } func (m *TaskStatus) String() string { return proto.CompactTextString(m) } func (*TaskStatus) ProtoMessage() {} func (*TaskStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{47} + return fileDescriptor_4a8f45747846a74d, []int{48} } func (m *TaskStatus) XXX_Unmarshal(b []byte) error { @@ -2928,7 +2992,7 @@ func (m *TaskDriverStatus) Reset() { *m = TaskDriverStatus{} } func (m *TaskDriverStatus) String() string { return proto.CompactTextString(m) } func (*TaskDriverStatus) ProtoMessage() {} func (*TaskDriverStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{48} + return fileDescriptor_4a8f45747846a74d, []int{49} } func (m *TaskDriverStatus) XXX_Unmarshal(b []byte) error { @@ -2974,7 +3038,7 @@ func (m *TaskStats) Reset() { *m = TaskStats{} } func (m *TaskStats) String() string { return proto.CompactTextString(m) } func (*TaskStats) ProtoMessage() {} func (*TaskStats) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{49} + return fileDescriptor_4a8f45747846a74d, []int{50} } func (m *TaskStats) XXX_Unmarshal(b []byte) error { @@ -3037,7 +3101,7 @@ func (m *TaskResourceUsage) Reset() { *m = TaskResourceUsage{} } func (m *TaskResourceUsage) String() string { return proto.CompactTextString(m) } func (*TaskResourceUsage) ProtoMessage() {} func (*TaskResourceUsage) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{50} + return fileDescriptor_4a8f45747846a74d, []int{51} } func (m *TaskResourceUsage) XXX_Unmarshal(b []byte) error { @@ -3090,7 +3154,7 @@ func (m *CPUUsage) Reset() { *m = CPUUsage{} } func (m *CPUUsage) String() string { return proto.CompactTextString(m) } func (*CPUUsage) ProtoMessage() {} func (*CPUUsage) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{51} + return fileDescriptor_4a8f45747846a74d, []int{52} } func (m *CPUUsage) XXX_Unmarshal(b []byte) error { @@ -3179,7 +3243,7 @@ func (m *MemoryUsage) Reset() { *m = MemoryUsage{} } func (m *MemoryUsage) String() string { return proto.CompactTextString(m) } func (*MemoryUsage) ProtoMessage() {} func (*MemoryUsage) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{52} + return fileDescriptor_4a8f45747846a74d, []int{53} } func (m *MemoryUsage) XXX_Unmarshal(b []byte) error { @@ -3278,7 +3342,7 @@ func (m *DriverTaskEvent) Reset() { *m = DriverTaskEvent{} } func (m *DriverTaskEvent) String() string { return proto.CompactTextString(m) } func (*DriverTaskEvent) ProtoMessage() {} func (*DriverTaskEvent) Descriptor() ([]byte, []int) { - return fileDescriptor_4a8f45747846a74d, []int{53} + return fileDescriptor_4a8f45747846a74d, []int{54} } func (m *DriverTaskEvent) XXX_Unmarshal(b []byte) error { @@ -3387,6 +3451,7 @@ func init() { proto.RegisterType((*DriverCapabilities)(nil), "hashicorp.nomad.plugins.drivers.proto.DriverCapabilities") proto.RegisterType((*NetworkIsolationSpec)(nil), "hashicorp.nomad.plugins.drivers.proto.NetworkIsolationSpec") proto.RegisterMapType((map[string]string)(nil), "hashicorp.nomad.plugins.drivers.proto.NetworkIsolationSpec.LabelsEntry") + proto.RegisterType((*DNSConfig)(nil), "hashicorp.nomad.plugins.drivers.proto.DNSConfig") proto.RegisterType((*TaskConfig)(nil), "hashicorp.nomad.plugins.drivers.proto.TaskConfig") proto.RegisterMapType((map[string]string)(nil), "hashicorp.nomad.plugins.drivers.proto.TaskConfig.DeviceEnvEntry") proto.RegisterMapType((map[string]string)(nil), "hashicorp.nomad.plugins.drivers.proto.TaskConfig.EnvEntry") @@ -3420,228 +3485,231 @@ func init() { } var fileDescriptor_4a8f45747846a74d = []byte{ - // 3525 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0x4f, 0x6f, 0x23, 0x47, - 0x76, 0x57, 0xf3, 0x9f, 0xc8, 0x47, 0x89, 0x6a, 0x95, 0xa4, 0x31, 0x87, 0x4e, 0xe2, 0xd9, 0x06, - 0x36, 0x10, 0x76, 0x6d, 0xca, 0xd6, 0x22, 0x1e, 0xcb, 0x3b, 0xde, 0x31, 0x4d, 0x71, 0x24, 0x79, - 0x24, 0x4a, 0x29, 0x52, 0x98, 0x9d, 0x38, 0xeb, 0x4e, 0xab, 0xbb, 0x86, 0xec, 0x11, 0xfb, 0x8f, - 0xbb, 0x8b, 0x1a, 0x69, 0x83, 0x20, 0xc1, 0x06, 0x08, 0x36, 0x40, 0x82, 0xe4, 0xe2, 0xec, 0x25, - 0x87, 0x60, 0x73, 0x4c, 0x3e, 0x40, 0x90, 0x60, 0xcf, 0xf9, 0x10, 0xc9, 0x25, 0xb7, 0x5c, 0x72, - 0xc8, 0x37, 0x58, 0xd4, 0x9f, 0x6e, 0x76, 0x8b, 0x1c, 0x4f, 0x93, 0x9a, 0x53, 0x77, 0xbd, 0xaa, - 0xfa, 0xd5, 0xab, 0xf7, 0x5e, 0xd5, 0x7b, 0x55, 0xf5, 0x40, 0xf3, 0x47, 0xe3, 0x81, 0xed, 0x86, - 0x3b, 0x56, 0x60, 0x5f, 0x91, 0x20, 0xdc, 0xf1, 0x03, 0x8f, 0x7a, 0xb2, 0xd4, 0xe4, 0x05, 0xf4, - 0xfd, 0xa1, 0x11, 0x0e, 0x6d, 0xd3, 0x0b, 0xfc, 0xa6, 0xeb, 0x39, 0x86, 0xd5, 0x94, 0x7d, 0x9a, - 0xb2, 0x8f, 0x68, 0xd6, 0xf8, 0xbd, 0x81, 0xe7, 0x0d, 0x46, 0x44, 0x20, 0x5c, 0x8c, 0x5f, 0xec, - 0x58, 0xe3, 0xc0, 0xa0, 0xb6, 0xe7, 0xca, 0xfa, 0xf7, 0x6e, 0xd7, 0x53, 0xdb, 0x21, 0x21, 0x35, - 0x1c, 0x5f, 0x36, 0xf8, 0x7c, 0x60, 0xd3, 0xe1, 0xf8, 0xa2, 0x69, 0x7a, 0xce, 0x4e, 0x3c, 0xe4, - 0x0e, 0x1f, 0x72, 0x27, 0x62, 0x33, 0x1c, 0x1a, 0x01, 0xb1, 0x76, 0x86, 0xe6, 0x28, 0xf4, 0x89, - 0xc9, 0xbe, 0x3a, 0xfb, 0x91, 0x08, 0x07, 0xd9, 0x11, 0x42, 0x1a, 0x8c, 0x4d, 0x1a, 0xcd, 0xd7, - 0xa0, 0x34, 0xb0, 0x2f, 0xc6, 0x94, 0x08, 0x20, 0xed, 0x3e, 0xbc, 0xd3, 0x37, 0xc2, 0xcb, 0xb6, - 0xe7, 0xbe, 0xb0, 0x07, 0x3d, 0x73, 0x48, 0x1c, 0x03, 0x93, 0x6f, 0xc6, 0x24, 0xa4, 0xda, 0x1f, - 0x43, 0x7d, 0xba, 0x2a, 0xf4, 0x3d, 0x37, 0x24, 0xe8, 0x73, 0x28, 0x30, 0x6e, 0xea, 0xca, 0x03, - 0x65, 0xbb, 0xba, 0xfb, 0x7e, 0xf3, 0x75, 0x82, 0x13, 0x3c, 0x34, 0xe5, 0x2c, 0x9a, 0x3d, 0x9f, - 0x98, 0x98, 0xf7, 0xd4, 0xb6, 0x60, 0xa3, 0x6d, 0xf8, 0xc6, 0x85, 0x3d, 0xb2, 0xa9, 0x4d, 0xc2, - 0x68, 0xd0, 0x31, 0x6c, 0xa6, 0xc9, 0x72, 0xc0, 0x9f, 0xc1, 0x8a, 0x99, 0xa0, 0xcb, 0x81, 0xf7, - 0x9a, 0x99, 0x34, 0xd6, 0xdc, 0xe7, 0xa5, 0x14, 0x70, 0x0a, 0x4e, 0xdb, 0x04, 0xf4, 0xc4, 0x76, - 0x07, 0x24, 0xf0, 0x03, 0xdb, 0xa5, 0x11, 0x33, 0xbf, 0xc9, 0xc3, 0x46, 0x8a, 0x2c, 0x99, 0x79, - 0x09, 0x10, 0xcb, 0x91, 0xb1, 0x92, 0xdf, 0xae, 0xee, 0x7e, 0x99, 0x91, 0x95, 0x19, 0x78, 0xcd, - 0x56, 0x0c, 0xd6, 0x71, 0x69, 0x70, 0x83, 0x13, 0xe8, 0xe8, 0x6b, 0x28, 0x0d, 0x89, 0x31, 0xa2, - 0xc3, 0x7a, 0xee, 0x81, 0xb2, 0x5d, 0xdb, 0x7d, 0x72, 0x87, 0x71, 0x0e, 0x39, 0x50, 0x8f, 0x1a, - 0x94, 0x60, 0x89, 0x8a, 0x3e, 0x00, 0x24, 0xfe, 0x74, 0x8b, 0x84, 0x66, 0x60, 0xfb, 0xcc, 0x90, - 0xeb, 0xf9, 0x07, 0xca, 0x76, 0x05, 0xaf, 0x8b, 0x9a, 0xfd, 0x49, 0x45, 0xc3, 0x87, 0xb5, 0x5b, - 0xdc, 0x22, 0x15, 0xf2, 0x97, 0xe4, 0x86, 0x6b, 0xa4, 0x82, 0xd9, 0x2f, 0x3a, 0x80, 0xe2, 0x95, - 0x31, 0x1a, 0x13, 0xce, 0x72, 0x75, 0xf7, 0xa3, 0x37, 0x99, 0x87, 0x34, 0xd1, 0x89, 0x1c, 0xb0, - 0xe8, 0xff, 0x69, 0xee, 0x13, 0x45, 0xdb, 0x83, 0x6a, 0x82, 0x6f, 0x54, 0x03, 0x38, 0xef, 0xee, - 0x77, 0xfa, 0x9d, 0x76, 0xbf, 0xb3, 0xaf, 0x2e, 0xa1, 0x55, 0xa8, 0x9c, 0x77, 0x0f, 0x3b, 0xad, - 0xe3, 0xfe, 0xe1, 0x73, 0x55, 0x41, 0x55, 0x58, 0x8e, 0x0a, 0x39, 0xed, 0x1a, 0x10, 0x26, 0xa6, - 0x77, 0x45, 0x02, 0x66, 0xc8, 0x52, 0xab, 0xe8, 0x1d, 0x58, 0xa6, 0x46, 0x78, 0xa9, 0xdb, 0x96, - 0xe4, 0xb9, 0xc4, 0x8a, 0x47, 0x16, 0x3a, 0x82, 0xd2, 0xd0, 0x70, 0xad, 0xd1, 0x9b, 0xf9, 0x4e, - 0x8b, 0x9a, 0x81, 0x1f, 0xf2, 0x8e, 0x58, 0x02, 0x30, 0xeb, 0x4e, 0x8d, 0x2c, 0x14, 0xa0, 0x3d, - 0x07, 0xb5, 0x47, 0x8d, 0x80, 0x26, 0xd9, 0xe9, 0x40, 0x81, 0x8d, 0x2f, 0x2d, 0x7a, 0x9e, 0x31, - 0xc5, 0xca, 0xc4, 0xbc, 0xbb, 0xf6, 0xff, 0x39, 0x58, 0x4f, 0x60, 0x4b, 0x4b, 0x7d, 0x06, 0xa5, - 0x80, 0x84, 0xe3, 0x11, 0xe5, 0xf0, 0xb5, 0xdd, 0xc7, 0x19, 0xe1, 0xa7, 0x90, 0x9a, 0x98, 0xc3, - 0x60, 0x09, 0x87, 0xb6, 0x41, 0x15, 0x3d, 0x74, 0x12, 0x04, 0x5e, 0xa0, 0x3b, 0xe1, 0x80, 0x4b, - 0xad, 0x82, 0x6b, 0x82, 0xde, 0x61, 0xe4, 0x93, 0x70, 0x90, 0x90, 0x6a, 0xfe, 0x8e, 0x52, 0x45, - 0x06, 0xa8, 0x2e, 0xa1, 0xaf, 0xbc, 0xe0, 0x52, 0x67, 0xa2, 0x0d, 0x6c, 0x8b, 0xd4, 0x0b, 0x1c, - 0xf4, 0xe3, 0x8c, 0xa0, 0x5d, 0xd1, 0xfd, 0x54, 0xf6, 0xc6, 0x6b, 0x6e, 0x9a, 0xa0, 0xfd, 0x10, - 0x4a, 0x62, 0xa6, 0xcc, 0x92, 0x7a, 0xe7, 0xed, 0x76, 0xa7, 0xd7, 0x53, 0x97, 0x50, 0x05, 0x8a, - 0xb8, 0xd3, 0xc7, 0xcc, 0xc2, 0x2a, 0x50, 0x7c, 0xd2, 0xea, 0xb7, 0x8e, 0xd5, 0x9c, 0xf6, 0x03, - 0x58, 0x7b, 0x66, 0xd8, 0x34, 0x8b, 0x71, 0x69, 0x1e, 0xa8, 0x93, 0xb6, 0x52, 0x3b, 0x47, 0x29, - 0xed, 0x64, 0x17, 0x4d, 0xe7, 0xda, 0xa6, 0xb7, 0xf4, 0xa1, 0x42, 0x9e, 0x04, 0x81, 0x54, 0x01, - 0xfb, 0xd5, 0x5e, 0xc1, 0x5a, 0x8f, 0x7a, 0x7e, 0x26, 0xcb, 0xff, 0x11, 0x2c, 0x33, 0x1f, 0xe5, - 0x8d, 0xa9, 0x34, 0xfd, 0xfb, 0x4d, 0xe1, 0xc3, 0x9a, 0x91, 0x0f, 0x6b, 0xee, 0x4b, 0x1f, 0x87, - 0xa3, 0x96, 0xe8, 0x1e, 0x94, 0x42, 0x7b, 0xe0, 0x1a, 0x23, 0xb9, 0x5b, 0xc8, 0x92, 0x86, 0x98, - 0x91, 0x47, 0x03, 0x4b, 0xc3, 0x6f, 0x03, 0xda, 0x27, 0x21, 0x0d, 0xbc, 0x9b, 0x4c, 0xfc, 0x6c, - 0x42, 0xf1, 0x85, 0x17, 0x98, 0x62, 0x21, 0x96, 0xb1, 0x28, 0xb0, 0x45, 0x95, 0x02, 0x91, 0xd8, - 0x1f, 0x00, 0x3a, 0x72, 0x99, 0x4f, 0xc9, 0xa6, 0x88, 0xbf, 0xcf, 0xc1, 0x46, 0xaa, 0xbd, 0x54, - 0xc6, 0xe2, 0xeb, 0x90, 0x6d, 0x4c, 0xe3, 0x50, 0xac, 0x43, 0x74, 0x0a, 0x25, 0xd1, 0x42, 0x4a, - 0xf2, 0xe1, 0x1c, 0x40, 0xc2, 0x4d, 0x49, 0x38, 0x09, 0x33, 0xd3, 0xe8, 0xf3, 0x6f, 0xd7, 0xe8, - 0x5f, 0x81, 0x1a, 0xcd, 0x23, 0x7c, 0xa3, 0x6e, 0xbe, 0x84, 0x0d, 0xd3, 0x1b, 0x8d, 0x88, 0xc9, - 0xac, 0x41, 0xb7, 0x5d, 0x4a, 0x82, 0x2b, 0x63, 0xf4, 0x66, 0xbb, 0x41, 0x93, 0x5e, 0x47, 0xb2, - 0x93, 0xf6, 0x15, 0xac, 0x27, 0x06, 0x96, 0x8a, 0x78, 0x02, 0xc5, 0x90, 0x11, 0xa4, 0x26, 0x3e, - 0x9c, 0x53, 0x13, 0x21, 0x16, 0xdd, 0xb5, 0x0d, 0x01, 0xde, 0xb9, 0x22, 0x6e, 0x3c, 0x2d, 0x6d, - 0x1f, 0xd6, 0x7b, 0xdc, 0x4c, 0x33, 0xd9, 0xe1, 0xc4, 0xc4, 0x73, 0x29, 0x13, 0xdf, 0x04, 0x94, - 0x44, 0x91, 0x86, 0x78, 0x03, 0x6b, 0x9d, 0x6b, 0x62, 0x66, 0x42, 0xae, 0xc3, 0xb2, 0xe9, 0x39, - 0x8e, 0xe1, 0x5a, 0xf5, 0xdc, 0x83, 0xfc, 0x76, 0x05, 0x47, 0xc5, 0xe4, 0x5a, 0xcc, 0x67, 0x5d, - 0x8b, 0xda, 0xdf, 0x2a, 0xa0, 0x4e, 0xc6, 0x96, 0x82, 0x64, 0xdc, 0x53, 0x8b, 0x01, 0xb1, 0xb1, - 0x57, 0xb0, 0x2c, 0x49, 0x7a, 0xb4, 0x5d, 0x08, 0x3a, 0x09, 0x82, 0xc4, 0x76, 0x94, 0xbf, 0xe3, - 0x76, 0xa4, 0x1d, 0xc2, 0xef, 0x44, 0xec, 0xf4, 0x68, 0x40, 0x0c, 0xc7, 0x76, 0x07, 0x47, 0xa7, - 0xa7, 0x3e, 0x11, 0x8c, 0x23, 0x04, 0x05, 0xcb, 0xa0, 0x86, 0x64, 0x8c, 0xff, 0xb3, 0x45, 0x6f, - 0x8e, 0xbc, 0x30, 0x5e, 0xf4, 0xbc, 0xa0, 0xfd, 0x67, 0x1e, 0xea, 0x53, 0x50, 0x91, 0x78, 0xbf, - 0x82, 0x62, 0x48, 0xe8, 0xd8, 0x97, 0xa6, 0xd2, 0xc9, 0xcc, 0xf0, 0x6c, 0xbc, 0x66, 0x8f, 0x81, - 0x61, 0x81, 0x89, 0x06, 0x50, 0xa6, 0xf4, 0x46, 0x0f, 0xed, 0x9f, 0x47, 0x01, 0xc1, 0xf1, 0x5d, - 0xf1, 0xfb, 0x24, 0x70, 0x6c, 0xd7, 0x18, 0xf5, 0xec, 0x9f, 0x13, 0xbc, 0x4c, 0xe9, 0x0d, 0xfb, - 0x41, 0xcf, 0x99, 0xc1, 0x5b, 0xb6, 0x2b, 0xc5, 0xde, 0x5e, 0x74, 0x94, 0x84, 0x80, 0xb1, 0x40, - 0x6c, 0x1c, 0x43, 0x91, 0xcf, 0x69, 0x11, 0x43, 0x54, 0x21, 0x4f, 0xe9, 0x0d, 0x67, 0xaa, 0x8c, - 0xd9, 0x6f, 0xe3, 0x11, 0xac, 0x24, 0x67, 0xc0, 0x0c, 0x69, 0x48, 0xec, 0xc1, 0x50, 0x18, 0x58, - 0x11, 0xcb, 0x12, 0xd3, 0xe4, 0x2b, 0xdb, 0x92, 0x21, 0x6b, 0x11, 0x8b, 0x82, 0xf6, 0x6f, 0x39, - 0xb8, 0x3f, 0x43, 0x32, 0xd2, 0x58, 0xbf, 0x4a, 0x19, 0xeb, 0x5b, 0x92, 0x42, 0x64, 0xf1, 0x5f, - 0xa5, 0x2c, 0xfe, 0x2d, 0x82, 0xb3, 0x65, 0x73, 0x0f, 0x4a, 0xe4, 0xda, 0xa6, 0xc4, 0x92, 0xa2, - 0x92, 0xa5, 0xc4, 0x72, 0x2a, 0xdc, 0x75, 0x39, 0x7d, 0x04, 0x9b, 0xed, 0x80, 0x18, 0x94, 0xc8, - 0xad, 0x3c, 0xb2, 0xff, 0xfb, 0x50, 0x36, 0x46, 0x23, 0xcf, 0x9c, 0xa8, 0x75, 0x99, 0x97, 0x8f, - 0x2c, 0xed, 0x5b, 0x05, 0xb6, 0x6e, 0xf5, 0x91, 0x92, 0xbe, 0x80, 0x9a, 0x1d, 0x7a, 0x23, 0x3e, - 0x09, 0x3d, 0x71, 0x8a, 0xfb, 0xf1, 0x7c, 0xee, 0xe4, 0x28, 0xc2, 0xe0, 0x87, 0xba, 0x55, 0x3b, - 0x59, 0xe4, 0x56, 0xc5, 0x07, 0xb7, 0xe4, 0x6a, 0x8e, 0x8a, 0xda, 0x3f, 0x28, 0xb0, 0x25, 0xbd, - 0x78, 0xe6, 0xc9, 0xcc, 0x60, 0x39, 0xf7, 0xb6, 0x59, 0xd6, 0xea, 0x70, 0xef, 0x36, 0x5f, 0x72, - 0x5f, 0xff, 0xa7, 0x3c, 0xa0, 0xe9, 0x13, 0x24, 0xfa, 0x1e, 0xac, 0x84, 0xc4, 0xb5, 0x74, 0xe1, - 0x13, 0x84, 0xbb, 0x2a, 0xe3, 0x2a, 0xa3, 0x09, 0xe7, 0x10, 0xb2, 0x6d, 0x8e, 0x5c, 0x4b, 0x6e, - 0xcb, 0x98, 0xff, 0xa3, 0x21, 0xac, 0xbc, 0x08, 0xf5, 0x78, 0x6c, 0x6e, 0x34, 0xb5, 0xcc, 0x5b, - 0xd7, 0x34, 0x1f, 0xcd, 0x27, 0xbd, 0x78, 0x5e, 0xb8, 0xfa, 0x22, 0x8c, 0x0b, 0xe8, 0x97, 0x0a, - 0xbc, 0x13, 0x85, 0x0e, 0x13, 0xf1, 0x39, 0x9e, 0x45, 0xc2, 0x7a, 0xe1, 0x41, 0x7e, 0xbb, 0xb6, - 0x7b, 0x76, 0x07, 0xf9, 0x4d, 0x11, 0x4f, 0x3c, 0x8b, 0xe0, 0x2d, 0x77, 0x06, 0x35, 0x44, 0x4d, - 0xd8, 0x70, 0xc6, 0x21, 0xd5, 0x85, 0x15, 0xe8, 0xb2, 0x51, 0xbd, 0xc8, 0xe5, 0xb2, 0xce, 0xaa, - 0x52, 0xb6, 0xaa, 0x35, 0xa1, 0x9a, 0x98, 0x16, 0x2a, 0x43, 0xa1, 0x7b, 0xda, 0xed, 0xa8, 0x4b, - 0x08, 0xa0, 0xd4, 0x3e, 0xc4, 0xa7, 0xa7, 0x7d, 0x11, 0x89, 0x1f, 0x9d, 0xb4, 0x0e, 0x3a, 0x6a, - 0x4e, 0xfb, 0xbf, 0x1c, 0x6c, 0xce, 0x62, 0x12, 0x59, 0x50, 0x60, 0x13, 0x96, 0xc7, 0x9f, 0xb7, - 0x3f, 0x5f, 0x8e, 0xce, 0xf4, 0xec, 0x1b, 0x72, 0xbf, 0xab, 0x60, 0xfe, 0x8f, 0x74, 0x28, 0x8d, - 0x8c, 0x0b, 0x32, 0x0a, 0xeb, 0x79, 0x7e, 0x41, 0x70, 0x70, 0x97, 0xb1, 0x8f, 0x39, 0x92, 0xb8, - 0x1d, 0x90, 0xb0, 0x8d, 0x3d, 0xa8, 0x26, 0xc8, 0x33, 0x8e, 0xe1, 0x9b, 0xc9, 0x63, 0x78, 0x25, - 0x79, 0xa6, 0x7e, 0x3c, 0x2d, 0x2d, 0x36, 0x1b, 0x26, 0xe7, 0xc3, 0xd3, 0x5e, 0x5f, 0x1c, 0x78, - 0x0e, 0xf0, 0xe9, 0xf9, 0x99, 0xaa, 0x30, 0x62, 0xbf, 0xd5, 0x7b, 0xaa, 0xe6, 0x62, 0x35, 0xe4, - 0xb5, 0x7f, 0x5d, 0x06, 0x98, 0x1c, 0x41, 0x51, 0x0d, 0x72, 0xf1, 0xa2, 0xcd, 0xd9, 0x16, 0x93, - 0x87, 0x6b, 0x38, 0xd1, 0xc0, 0xfc, 0x1f, 0xed, 0xc2, 0x96, 0x13, 0x0e, 0x7c, 0xc3, 0xbc, 0xd4, - 0xe5, 0xc9, 0xd1, 0xe4, 0x9d, 0xf9, 0x02, 0x58, 0xc1, 0x1b, 0xb2, 0x52, 0x1a, 0xb8, 0xc0, 0x3d, - 0x86, 0x3c, 0x71, 0xaf, 0xb8, 0xb1, 0x56, 0x77, 0x3f, 0x9d, 0xfb, 0x68, 0xdc, 0xec, 0xb8, 0x57, - 0x42, 0x66, 0x0c, 0x06, 0xe9, 0x00, 0x16, 0xb9, 0xb2, 0x4d, 0xa2, 0x33, 0xd0, 0x22, 0x07, 0xfd, - 0x7c, 0x7e, 0xd0, 0x7d, 0x8e, 0x11, 0x43, 0x57, 0xac, 0xa8, 0x8c, 0xba, 0x50, 0x09, 0x48, 0xe8, - 0x8d, 0x03, 0x93, 0x84, 0xf5, 0xd2, 0x5c, 0xd1, 0x2b, 0x8e, 0xfa, 0xe1, 0x09, 0x04, 0xda, 0x87, - 0x92, 0xe3, 0x8d, 0x5d, 0x1a, 0xd6, 0x97, 0x39, 0xb3, 0xef, 0x67, 0x04, 0x3b, 0x61, 0x9d, 0xb0, - 0xec, 0x8b, 0x0e, 0x60, 0x59, 0xb0, 0x18, 0xd6, 0xcb, 0x1c, 0xe6, 0x83, 0xac, 0x7b, 0x0d, 0xef, - 0x85, 0xa3, 0xde, 0x4c, 0xab, 0xe3, 0x90, 0x04, 0xf5, 0x8a, 0xd0, 0x2a, 0xfb, 0x47, 0xef, 0x42, - 0x45, 0x6c, 0xda, 0x96, 0x1d, 0xd4, 0x81, 0x57, 0x88, 0x5d, 0x7c, 0xdf, 0x0e, 0xd0, 0x7b, 0x50, - 0x15, 0x0e, 0x58, 0xe7, 0xab, 0xa3, 0xca, 0xab, 0x41, 0x90, 0xce, 0xd8, 0x1a, 0x11, 0x0d, 0x48, - 0x10, 0x88, 0x06, 0x2b, 0x71, 0x03, 0x12, 0x04, 0xbc, 0xc1, 0xef, 0xc3, 0x1a, 0x0f, 0x5b, 0x06, - 0x81, 0x37, 0xf6, 0x75, 0x6e, 0x53, 0xab, 0xbc, 0xd1, 0x2a, 0x23, 0x1f, 0x30, 0x6a, 0x97, 0x19, - 0xd7, 0x7d, 0x28, 0xbf, 0xf4, 0x2e, 0x44, 0x83, 0x9a, 0xf0, 0x1d, 0x2f, 0xbd, 0x8b, 0xa8, 0x2a, - 0x76, 0x2b, 0x6b, 0x69, 0xb7, 0xf2, 0x0d, 0xdc, 0x9b, 0xde, 0x1f, 0xb9, 0x7b, 0x51, 0xef, 0xee, - 0x5e, 0x36, 0xdd, 0x19, 0xd4, 0xc6, 0xc7, 0x50, 0x8e, 0x2c, 0x67, 0x9e, 0x15, 0xdb, 0x78, 0x04, - 0xb5, 0xb4, 0xdd, 0xcd, 0xb5, 0xde, 0xff, 0x4b, 0x81, 0x4a, 0x6c, 0x61, 0xc8, 0x85, 0x0d, 0x2e, - 0x01, 0xe6, 0x8f, 0xf5, 0x89, 0xc1, 0x8a, 0x28, 0xe0, 0xb3, 0x8c, 0x73, 0x6e, 0x45, 0x08, 0xf2, - 0xc8, 0x21, 0xad, 0x17, 0xc5, 0xc8, 0x93, 0xf1, 0xbe, 0x86, 0xb5, 0x91, 0xed, 0x8e, 0xaf, 0x13, - 0x63, 0x09, 0xf7, 0xfd, 0x07, 0x19, 0xc7, 0x3a, 0x66, 0xbd, 0x27, 0x63, 0xd4, 0x46, 0xa9, 0xb2, - 0xf6, 0x6d, 0x0e, 0xee, 0xcd, 0x66, 0x07, 0x75, 0x21, 0x6f, 0xfa, 0x63, 0x39, 0xb5, 0x47, 0xf3, - 0x4e, 0xad, 0xed, 0x8f, 0x27, 0xa3, 0x32, 0x20, 0xf4, 0x0c, 0x4a, 0x0e, 0x71, 0xbc, 0xe0, 0x46, - 0xce, 0xe0, 0xf1, 0xbc, 0x90, 0x27, 0xbc, 0xf7, 0x04, 0x55, 0xc2, 0x21, 0x0c, 0x65, 0x69, 0x2f, - 0xa1, 0xdc, 0x99, 0xe6, 0x3c, 0xdd, 0x47, 0x90, 0x38, 0xc6, 0xd1, 0x3e, 0x86, 0xad, 0x99, 0x53, - 0x41, 0xbf, 0x0b, 0x60, 0xfa, 0x63, 0x9d, 0xdf, 0xbe, 0x0a, 0xbd, 0xe7, 0x71, 0xc5, 0xf4, 0xc7, - 0x3d, 0x4e, 0xd0, 0x1e, 0x42, 0xfd, 0x75, 0xfc, 0xb2, 0xf5, 0x2e, 0x38, 0xd6, 0x9d, 0x0b, 0x2e, - 0x83, 0x3c, 0x2e, 0x0b, 0xc2, 0xc9, 0x85, 0xf6, 0xab, 0x1c, 0xac, 0xdd, 0x62, 0x87, 0x45, 0xc7, - 0x62, 0xff, 0x88, 0xce, 0x1d, 0xa2, 0xc4, 0x36, 0x13, 0xd3, 0xb6, 0xa2, 0x1b, 0x2b, 0xfe, 0xcf, - 0xdd, 0x88, 0x2f, 0x6f, 0x93, 0x72, 0xb6, 0xcf, 0x0c, 0xda, 0xb9, 0xb0, 0x69, 0xc8, 0x03, 0xe8, - 0x22, 0x16, 0x05, 0xf4, 0x1c, 0x6a, 0x01, 0x09, 0x49, 0x70, 0x45, 0x2c, 0xdd, 0xf7, 0x02, 0x1a, - 0x09, 0x6c, 0x77, 0x3e, 0x81, 0x9d, 0x79, 0x01, 0xc5, 0xab, 0x11, 0x12, 0x2b, 0x85, 0xe8, 0x19, - 0xac, 0x5a, 0x37, 0xae, 0xe1, 0xd8, 0xa6, 0x44, 0x2e, 0x2d, 0x8c, 0xbc, 0x22, 0x81, 0x38, 0xb0, - 0xb6, 0x07, 0xd5, 0x44, 0x25, 0x9b, 0x18, 0x77, 0xe2, 0x52, 0x26, 0xa2, 0x90, 0x5e, 0xbf, 0x45, - 0xb9, 0x7e, 0xb5, 0x7f, 0xce, 0x41, 0x2d, 0xbd, 0x00, 0x22, 0xfd, 0xf9, 0x24, 0xb0, 0x3d, 0x2b, - 0xa1, 0xbf, 0x33, 0x4e, 0x60, 0x3a, 0x62, 0xd5, 0xdf, 0x8c, 0x3d, 0x6a, 0x44, 0x3a, 0x32, 0xfd, - 0xf1, 0x1f, 0xb2, 0xf2, 0x2d, 0xdd, 0xe7, 0x6f, 0xe9, 0x1e, 0xbd, 0x0f, 0x48, 0xea, 0x77, 0x64, - 0x3b, 0x36, 0xd5, 0x2f, 0x6e, 0x28, 0x11, 0xf2, 0xcf, 0x63, 0x55, 0xd4, 0x1c, 0xb3, 0x8a, 0x2f, - 0x18, 0x1d, 0x69, 0xb0, 0xea, 0x79, 0x8e, 0x1e, 0x9a, 0x5e, 0x40, 0x74, 0xc3, 0x7a, 0xc9, 0x03, - 0xba, 0x3c, 0xae, 0x7a, 0x9e, 0xd3, 0x63, 0xb4, 0x96, 0xf5, 0x92, 0xed, 0xf1, 0xa6, 0x3f, 0x0e, - 0x09, 0xd5, 0xd9, 0x87, 0xbb, 0xc5, 0x0a, 0x06, 0x41, 0x6a, 0xfb, 0xe3, 0x30, 0xd1, 0xc0, 0x21, - 0x0e, 0x73, 0x75, 0x89, 0x06, 0x27, 0xc4, 0x61, 0xa3, 0xac, 0x9c, 0x91, 0xc0, 0x24, 0x2e, 0xed, - 0xdb, 0xe6, 0x25, 0xf3, 0x62, 0xca, 0xb6, 0x82, 0x53, 0x34, 0xed, 0x67, 0x50, 0xe4, 0x5e, 0x8f, - 0x4d, 0x9e, 0x7b, 0x0c, 0xee, 0x50, 0x84, 0x78, 0xcb, 0x8c, 0xc0, 0xdd, 0xc9, 0xbb, 0x50, 0x19, - 0x7a, 0xa1, 0x74, 0x47, 0xc2, 0xf2, 0xca, 0x8c, 0xc0, 0x2b, 0x1b, 0x50, 0x0e, 0x88, 0x61, 0x79, - 0xee, 0x28, 0x3a, 0xf4, 0xc6, 0x65, 0xed, 0x1b, 0x28, 0x89, 0xed, 0xf7, 0x0e, 0xf8, 0x1f, 0x00, - 0x32, 0x85, 0x1f, 0xf3, 0xd9, 0x21, 0x3a, 0x0c, 0x6d, 0xcf, 0x0d, 0xa3, 0x97, 0x16, 0x51, 0x73, - 0x36, 0xa9, 0xd0, 0xfe, 0x5b, 0x11, 0x21, 0x96, 0xb8, 0x03, 0x67, 0x27, 0x2a, 0x66, 0x69, 0xec, - 0xc4, 0x20, 0x0e, 0xdb, 0x51, 0x91, 0x9d, 0x33, 0x65, 0x24, 0x95, 0x5b, 0xf4, 0x09, 0x41, 0x02, - 0x44, 0x57, 0x6f, 0x44, 0x1e, 0x4a, 0xe6, 0xbd, 0x7a, 0x23, 0xe2, 0xea, 0x8d, 0xb0, 0xa3, 0x91, - 0x8c, 0xf1, 0x04, 0x5c, 0x81, 0x87, 0x78, 0x55, 0x2b, 0xbe, 0xdf, 0x24, 0xda, 0xff, 0x2a, 0xf1, - 0x5e, 0x11, 0xdd, 0x43, 0xa2, 0xaf, 0xa1, 0xcc, 0x96, 0x9d, 0xee, 0x18, 0xbe, 0x7c, 0x55, 0x6b, - 0x2f, 0x76, 0xc5, 0xd9, 0x64, 0xab, 0xec, 0xc4, 0xf0, 0x45, 0x84, 0xb6, 0xec, 0x8b, 0x12, 0xdb, - 0x73, 0x0c, 0x6b, 0xb2, 0xe7, 0xb0, 0x7f, 0xf4, 0x7d, 0xa8, 0x19, 0x63, 0xea, 0xe9, 0x86, 0x75, - 0x45, 0x02, 0x6a, 0x87, 0x44, 0xea, 0x7e, 0x95, 0x51, 0x5b, 0x11, 0xb1, 0xf1, 0x29, 0xac, 0x24, - 0x31, 0xdf, 0xe4, 0x7d, 0x8b, 0x49, 0xef, 0xfb, 0x27, 0x00, 0x93, 0x33, 0x3d, 0xb3, 0x11, 0x72, - 0x6d, 0x53, 0xdd, 0x8c, 0x8e, 0x25, 0x45, 0x5c, 0x66, 0x84, 0x36, 0x0b, 0xc0, 0xd3, 0x17, 0x8e, - 0xc5, 0xe8, 0xc2, 0x91, 0xad, 0x5a, 0xb6, 0xd0, 0x2e, 0xed, 0xd1, 0x28, 0xbe, 0x67, 0xa8, 0x78, - 0x9e, 0xf3, 0x94, 0x13, 0xb4, 0xdf, 0xe4, 0x84, 0xad, 0x88, 0xab, 0xe3, 0x4c, 0xe1, 0xf8, 0xdb, - 0x52, 0xf5, 0x1e, 0x40, 0x48, 0x8d, 0x80, 0x85, 0x12, 0x46, 0x74, 0xd3, 0xd1, 0x98, 0xba, 0xb1, - 0xec, 0x47, 0x2f, 0xe0, 0xb8, 0x22, 0x5b, 0xb7, 0x28, 0xfa, 0x0c, 0x56, 0x4c, 0xcf, 0xf1, 0x47, - 0x44, 0x76, 0x2e, 0xbe, 0xb1, 0x73, 0x35, 0x6e, 0xdf, 0xa2, 0x89, 0xfb, 0x95, 0xd2, 0x5d, 0xef, - 0x57, 0xfe, 0x5d, 0x11, 0x37, 0xe0, 0xc9, 0x0b, 0x78, 0x34, 0x98, 0xf1, 0xca, 0x7b, 0xb0, 0xe0, - 0x6d, 0xfe, 0x77, 0x3d, 0xf1, 0x36, 0x3e, 0xcb, 0xf2, 0xa6, 0xfa, 0xfa, 0xe0, 0xee, 0x3f, 0xf2, - 0x50, 0x89, 0x2f, 0xbf, 0xa7, 0x74, 0xff, 0x09, 0x54, 0xe2, 0xf4, 0x03, 0xb9, 0x41, 0x7c, 0xa7, - 0x7a, 0xe2, 0xc6, 0xe8, 0x05, 0x20, 0x63, 0x30, 0x88, 0x83, 0x36, 0x7d, 0x1c, 0x1a, 0x83, 0xe8, - 0xe9, 0xe1, 0x93, 0x39, 0xe4, 0x10, 0xf9, 0xad, 0x73, 0xd6, 0x1f, 0xab, 0xc6, 0x60, 0x90, 0xa2, - 0xa0, 0x3f, 0x85, 0xad, 0xf4, 0x18, 0xfa, 0xc5, 0x8d, 0xee, 0xdb, 0x96, 0x3c, 0xf6, 0x1d, 0xce, - 0x7b, 0xff, 0xdf, 0x4c, 0xc1, 0x7f, 0x71, 0x73, 0x66, 0x5b, 0x42, 0xe6, 0x28, 0x98, 0xaa, 0x68, - 0xfc, 0x39, 0xbc, 0xf3, 0x9a, 0xe6, 0x33, 0x74, 0xd0, 0x4d, 0xbf, 0x6b, 0x2f, 0x2e, 0x84, 0x84, - 0xf6, 0x7e, 0xad, 0x88, 0x67, 0x8a, 0xb4, 0x4c, 0x5a, 0xc9, 0xb8, 0x75, 0x27, 0xe3, 0x38, 0xed, - 0xb3, 0x73, 0x01, 0xcf, 0x43, 0xd5, 0x2f, 0x6f, 0x85, 0xaa, 0x59, 0x83, 0x18, 0x11, 0xf1, 0x09, - 0x20, 0x89, 0xa0, 0xfd, 0x4b, 0x1e, 0xca, 0x11, 0x3a, 0x3f, 0xb4, 0xdd, 0x84, 0x94, 0x38, 0x7a, - 0x7c, 0xb3, 0xa2, 0x60, 0x10, 0x24, 0x7e, 0x8b, 0xf0, 0x2e, 0x54, 0xd8, 0xd9, 0x50, 0x54, 0xe7, - 0x78, 0x75, 0x99, 0x11, 0x78, 0xe5, 0x7b, 0x50, 0xa5, 0x1e, 0x35, 0x46, 0x3a, 0xe5, 0xbe, 0x3c, - 0x2f, 0x7a, 0x73, 0x12, 0xf7, 0xe4, 0xe8, 0x87, 0xb0, 0x4e, 0x87, 0x81, 0x47, 0xe9, 0x88, 0xc5, - 0x77, 0x3c, 0xa2, 0x11, 0x01, 0x48, 0x01, 0xab, 0x71, 0x85, 0x88, 0x74, 0x42, 0xb6, 0x7b, 0x4f, - 0x1a, 0x33, 0xd3, 0xe5, 0x9b, 0x48, 0x01, 0xaf, 0xc6, 0x54, 0x66, 0xda, 0xcc, 0x79, 0xfa, 0x22, - 0x5a, 0xe0, 0x7b, 0x85, 0x82, 0xa3, 0x22, 0xd2, 0x61, 0xcd, 0x21, 0x46, 0x38, 0x0e, 0x88, 0xa5, - 0xbf, 0xb0, 0xc9, 0xc8, 0x12, 0x67, 0xed, 0x5a, 0xe6, 0xf0, 0x3b, 0x12, 0x4b, 0xf3, 0x09, 0xef, - 0x8d, 0x6b, 0x11, 0x9c, 0x28, 0xb3, 0xc8, 0x41, 0xfc, 0xa1, 0x35, 0xa8, 0xf6, 0x9e, 0xf7, 0xfa, - 0x9d, 0x13, 0xfd, 0xe4, 0x74, 0xbf, 0x23, 0x53, 0x17, 0x7a, 0x1d, 0x2c, 0x8a, 0x0a, 0xab, 0xef, - 0x9f, 0xf6, 0x5b, 0xc7, 0x7a, 0xff, 0xa8, 0xfd, 0xb4, 0xa7, 0xe6, 0xd0, 0x16, 0xac, 0xf7, 0x0f, - 0xf1, 0x69, 0xbf, 0x7f, 0xdc, 0xd9, 0xd7, 0xcf, 0x3a, 0xf8, 0xe8, 0x74, 0xbf, 0xa7, 0xe6, 0x11, - 0x82, 0xda, 0x84, 0xdc, 0x3f, 0x3a, 0xe9, 0xa8, 0x05, 0x54, 0x85, 0xe5, 0xb3, 0x0e, 0x6e, 0x77, - 0xba, 0x7d, 0xb5, 0xa8, 0xfd, 0x2a, 0x0f, 0xd5, 0x84, 0x16, 0x99, 0x21, 0x07, 0xa1, 0x88, 0xf3, - 0x0b, 0x98, 0xfd, 0xf2, 0xa7, 0x16, 0xc3, 0x1c, 0x0a, 0xed, 0x14, 0xb0, 0x28, 0xf0, 0xd8, 0xde, - 0xb8, 0x4e, 0xac, 0xf3, 0x02, 0x2e, 0x3b, 0xc6, 0xb5, 0x00, 0xf9, 0x1e, 0xac, 0x5c, 0x92, 0xc0, - 0x25, 0x23, 0x59, 0x2f, 0x34, 0x52, 0x15, 0x34, 0xd1, 0x64, 0x1b, 0x54, 0xd9, 0x64, 0x02, 0x23, - 0xd4, 0x51, 0x13, 0xf4, 0x93, 0x08, 0x6c, 0x13, 0x8a, 0xa2, 0x7a, 0x59, 0x8c, 0xcf, 0x0b, 0xcc, - 0x4d, 0x85, 0xaf, 0x0c, 0x9f, 0xc7, 0x77, 0x05, 0xcc, 0xff, 0xd1, 0xc5, 0xb4, 0x7e, 0x4a, 0x5c, - 0x3f, 0x7b, 0xf3, 0x9b, 0xf3, 0xeb, 0x54, 0x34, 0x8c, 0x55, 0xb4, 0x0c, 0x79, 0x1c, 0xbd, 0xf7, - 0xb7, 0x5b, 0xed, 0x43, 0xa6, 0x96, 0x55, 0xa8, 0x9c, 0xb4, 0x7e, 0xaa, 0x9f, 0xf7, 0xf8, 0x4d, - 0x23, 0x52, 0x61, 0xe5, 0x69, 0x07, 0x77, 0x3b, 0xc7, 0x92, 0x92, 0x47, 0x9b, 0xa0, 0x4a, 0xca, - 0xa4, 0x5d, 0x81, 0x21, 0x88, 0xdf, 0x22, 0x2a, 0x43, 0xa1, 0xf7, 0xac, 0x75, 0xa6, 0x96, 0xb4, - 0xff, 0xc9, 0xc1, 0x9a, 0x70, 0x0b, 0xf1, 0xcb, 0xe4, 0xeb, 0x5f, 0x66, 0x92, 0x17, 0x17, 0xb9, - 0xf4, 0xc5, 0x45, 0x14, 0x84, 0x72, 0xaf, 0x9e, 0x9f, 0x04, 0xa1, 0xfc, 0xc2, 0x23, 0xb5, 0xe3, - 0x17, 0xe6, 0xd9, 0xf1, 0xeb, 0xb0, 0xec, 0x90, 0x30, 0xd6, 0x5b, 0x05, 0x47, 0x45, 0x64, 0x43, - 0xd5, 0x70, 0x5d, 0x8f, 0xf2, 0x8b, 0x8c, 0xe8, 0x58, 0x74, 0x30, 0xd7, 0x9d, 0x75, 0x3c, 0xe3, - 0x66, 0x6b, 0x82, 0x24, 0x36, 0xe6, 0x24, 0x76, 0xe3, 0x27, 0xa0, 0xde, 0x6e, 0x30, 0x8f, 0x3b, - 0xfc, 0xc1, 0x47, 0x13, 0x6f, 0x48, 0xd8, 0xba, 0x38, 0xef, 0x3e, 0xed, 0x9e, 0x3e, 0xeb, 0xaa, - 0x4b, 0xac, 0x80, 0xcf, 0xbb, 0xdd, 0xa3, 0xee, 0x81, 0xaa, 0x20, 0x80, 0x52, 0xe7, 0xa7, 0x47, - 0xfd, 0xce, 0xbe, 0x9a, 0xdb, 0xfd, 0xf5, 0x3a, 0x94, 0x04, 0x93, 0xe8, 0x5b, 0x19, 0x09, 0x24, - 0xb3, 0xde, 0xd0, 0x4f, 0xe6, 0x8e, 0xa8, 0x53, 0x99, 0x74, 0x8d, 0xc7, 0x0b, 0xf7, 0x97, 0x2f, - 0x10, 0x4b, 0xe8, 0xaf, 0x15, 0x58, 0x49, 0xbd, 0x3e, 0x64, 0xbd, 0x0d, 0x9d, 0x91, 0x64, 0xd7, - 0xf8, 0xf1, 0x42, 0x7d, 0x63, 0x5e, 0x7e, 0xa9, 0x40, 0x35, 0x91, 0x5e, 0x86, 0xf6, 0x16, 0x49, - 0x49, 0x13, 0x9c, 0x7c, 0xba, 0x78, 0x36, 0x9b, 0xb6, 0xf4, 0xa1, 0x82, 0xfe, 0x4a, 0x81, 0x6a, - 0x22, 0xd1, 0x2a, 0x33, 0x2b, 0xd3, 0x69, 0x61, 0x99, 0x59, 0x99, 0x95, 0xd7, 0xb5, 0x84, 0xfe, - 0x42, 0x81, 0x4a, 0x9c, 0x34, 0x85, 0x1e, 0xce, 0x9f, 0x66, 0x25, 0x98, 0xf8, 0x64, 0xd1, 0xfc, - 0x2c, 0x6d, 0x09, 0xfd, 0x19, 0x94, 0xa3, 0x0c, 0x23, 0x94, 0xd5, 0x7b, 0xdd, 0x4a, 0x5f, 0x6a, - 0x3c, 0x9c, 0xbb, 0x5f, 0x72, 0xf8, 0x28, 0xed, 0x27, 0xf3, 0xf0, 0xb7, 0x12, 0x94, 0x1a, 0x0f, - 0xe7, 0xee, 0x17, 0x0f, 0xcf, 0x2c, 0x21, 0x91, 0x1d, 0x94, 0xd9, 0x12, 0xa6, 0xd3, 0x92, 0x32, - 0x5b, 0xc2, 0xac, 0x64, 0x24, 0xc1, 0x48, 0x22, 0xbf, 0x28, 0x33, 0x23, 0xd3, 0x39, 0x4c, 0x99, - 0x19, 0x99, 0x91, 0xce, 0xa4, 0x2d, 0xa1, 0x5f, 0x28, 0xc9, 0x73, 0xc1, 0xc3, 0xb9, 0xd3, 0x68, - 0xe6, 0x34, 0xc9, 0xa9, 0x44, 0x1e, 0xbe, 0x40, 0x7f, 0x21, 0x6f, 0x31, 0x44, 0x16, 0x0e, 0x9a, - 0x07, 0x2c, 0x95, 0xb8, 0xd3, 0xf8, 0x78, 0x31, 0x67, 0xc3, 0x99, 0xf8, 0x4b, 0x05, 0x60, 0x92, - 0xaf, 0x93, 0x99, 0x89, 0xa9, 0x44, 0xa1, 0xc6, 0xde, 0x02, 0x3d, 0x93, 0x0b, 0x24, 0xca, 0x27, - 0xc8, 0xbc, 0x40, 0x6e, 0xe5, 0x13, 0x65, 0x5e, 0x20, 0xb7, 0x73, 0x81, 0xb4, 0x25, 0xf4, 0x8f, - 0x0a, 0xac, 0x4f, 0xe5, 0x33, 0xa0, 0xc7, 0x77, 0x4c, 0x69, 0x69, 0x7c, 0xbe, 0x38, 0x40, 0xc4, - 0xda, 0xb6, 0xf2, 0xa1, 0x82, 0xfe, 0x46, 0x81, 0xd5, 0xd4, 0x1b, 0x30, 0xca, 0xec, 0xa5, 0x66, - 0x64, 0x46, 0x34, 0x1e, 0x2d, 0xd6, 0x39, 0x96, 0xd6, 0xdf, 0x29, 0x50, 0x4b, 0xa7, 0x03, 0xa0, - 0x47, 0xf3, 0x6d, 0x0b, 0xb7, 0x18, 0xfa, 0x6c, 0xc1, 0xde, 0x11, 0x47, 0x5f, 0x2c, 0xff, 0x51, - 0x51, 0x44, 0x6f, 0x25, 0xfe, 0xf9, 0xd1, 0x6f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x81, 0xc8, 0x42, - 0x4f, 0xd2, 0x30, 0x00, 0x00, + // 3582 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0x4f, 0x6f, 0x1b, 0x49, + 0x76, 0x57, 0xb3, 0x49, 0x8a, 0x7c, 0x94, 0xa8, 0x56, 0x59, 0xf6, 0xd0, 0x9c, 0x24, 0xe3, 0x6d, + 0x60, 0x03, 0x61, 0x77, 0x86, 0x9e, 0xd1, 0x22, 0xe3, 0xf1, 0xac, 0x67, 0x3d, 0x1c, 0x8a, 0x96, + 0x34, 0x96, 0x28, 0xa5, 0x48, 0xc1, 0xeb, 0x38, 0x3b, 0x9d, 0x56, 0x77, 0x99, 0x6c, 0x9b, 0xfd, + 0xc7, 0xdd, 0x45, 0x59, 0xda, 0x20, 0x48, 0xb0, 0x01, 0x82, 0x0d, 0x90, 0x20, 0xb9, 0x4c, 0xf6, + 0x92, 0x43, 0xb0, 0x39, 0xe6, 0x0b, 0x04, 0x09, 0xf6, 0x9c, 0x0f, 0x91, 0x5c, 0x72, 0xcb, 0x25, + 0x40, 0xf2, 0x0d, 0x16, 0xf5, 0xa7, 0x9b, 0xdd, 0x22, 0x3d, 0x6e, 0x52, 0x3e, 0x75, 0xd7, 0xab, + 0xaa, 0x5f, 0xbd, 0x7a, 0xef, 0x55, 0xbd, 0x57, 0x55, 0x0f, 0xf4, 0x60, 0x3c, 0x19, 0x3a, 0x5e, + 0x74, 0xd7, 0x0e, 0x9d, 0x73, 0x12, 0x46, 0x77, 0x83, 0xd0, 0xa7, 0xbe, 0x2c, 0xb5, 0x78, 0x01, + 0x7d, 0x7f, 0x64, 0x46, 0x23, 0xc7, 0xf2, 0xc3, 0xa0, 0xe5, 0xf9, 0xae, 0x69, 0xb7, 0x64, 0x9f, + 0x96, 0xec, 0x23, 0x9a, 0x35, 0x7f, 0x6f, 0xe8, 0xfb, 0xc3, 0x31, 0x11, 0x08, 0x67, 0x93, 0xe7, + 0x77, 0xed, 0x49, 0x68, 0x52, 0xc7, 0xf7, 0x64, 0xfd, 0x07, 0x57, 0xeb, 0xa9, 0xe3, 0x92, 0x88, + 0x9a, 0x6e, 0x20, 0x1b, 0x7c, 0x39, 0x74, 0xe8, 0x68, 0x72, 0xd6, 0xb2, 0x7c, 0xf7, 0x6e, 0x32, + 0xe4, 0x5d, 0x3e, 0xe4, 0xdd, 0x98, 0xcd, 0x68, 0x64, 0x86, 0xc4, 0xbe, 0x3b, 0xb2, 0xc6, 0x51, + 0x40, 0x2c, 0xf6, 0x35, 0xd8, 0x8f, 0x44, 0xd8, 0xcb, 0x8f, 0x10, 0xd1, 0x70, 0x62, 0xd1, 0x78, + 0xbe, 0x26, 0xa5, 0xa1, 0x73, 0x36, 0xa1, 0x44, 0x00, 0xe9, 0xb7, 0xe1, 0xbd, 0x81, 0x19, 0xbd, + 0xec, 0xf8, 0xde, 0x73, 0x67, 0xd8, 0xb7, 0x46, 0xc4, 0x35, 0x31, 0x79, 0x35, 0x21, 0x11, 0xd5, + 0xff, 0x18, 0x1a, 0xb3, 0x55, 0x51, 0xe0, 0x7b, 0x11, 0x41, 0x5f, 0x42, 0x91, 0x71, 0xd3, 0x50, + 0xee, 0x28, 0xdb, 0xb5, 0x9d, 0x0f, 0x5b, 0x6f, 0x12, 0x9c, 0xe0, 0xa1, 0x25, 0x67, 0xd1, 0xea, + 0x07, 0xc4, 0xc2, 0xbc, 0xa7, 0x7e, 0x13, 0x6e, 0x74, 0xcc, 0xc0, 0x3c, 0x73, 0xc6, 0x0e, 0x75, + 0x48, 0x14, 0x0f, 0x3a, 0x81, 0xad, 0x2c, 0x59, 0x0e, 0xf8, 0x33, 0x58, 0xb3, 0x52, 0x74, 0x39, + 0xf0, 0xfd, 0x56, 0x2e, 0x8d, 0xb5, 0x76, 0x79, 0x29, 0x03, 0x9c, 0x81, 0xd3, 0xb7, 0x00, 0x3d, + 0x72, 0xbc, 0x21, 0x09, 0x83, 0xd0, 0xf1, 0x68, 0xcc, 0xcc, 0x6f, 0x54, 0xb8, 0x91, 0x21, 0x4b, + 0x66, 0x5e, 0x00, 0x24, 0x72, 0x64, 0xac, 0xa8, 0xdb, 0xb5, 0x9d, 0xaf, 0x73, 0xb2, 0x32, 0x07, + 0xaf, 0xd5, 0x4e, 0xc0, 0xba, 0x1e, 0x0d, 0x2f, 0x71, 0x0a, 0x1d, 0x7d, 0x03, 0xe5, 0x11, 0x31, + 0xc7, 0x74, 0xd4, 0x28, 0xdc, 0x51, 0xb6, 0xeb, 0x3b, 0x8f, 0xae, 0x31, 0xce, 0x3e, 0x07, 0xea, + 0x53, 0x93, 0x12, 0x2c, 0x51, 0xd1, 0x47, 0x80, 0xc4, 0x9f, 0x61, 0x93, 0xc8, 0x0a, 0x9d, 0x80, + 0x19, 0x72, 0x43, 0xbd, 0xa3, 0x6c, 0x57, 0xf1, 0xa6, 0xa8, 0xd9, 0x9d, 0x56, 0x34, 0x03, 0xd8, + 0xb8, 0xc2, 0x2d, 0xd2, 0x40, 0x7d, 0x49, 0x2e, 0xb9, 0x46, 0xaa, 0x98, 0xfd, 0xa2, 0x3d, 0x28, + 0x9d, 0x9b, 0xe3, 0x09, 0xe1, 0x2c, 0xd7, 0x76, 0x3e, 0x79, 0x9b, 0x79, 0x48, 0x13, 0x9d, 0xca, + 0x01, 0x8b, 0xfe, 0x9f, 0x17, 0x3e, 0x53, 0xf4, 0xfb, 0x50, 0x4b, 0xf1, 0x8d, 0xea, 0x00, 0xa7, + 0xbd, 0xdd, 0xee, 0xa0, 0xdb, 0x19, 0x74, 0x77, 0xb5, 0x15, 0xb4, 0x0e, 0xd5, 0xd3, 0xde, 0x7e, + 0xb7, 0x7d, 0x38, 0xd8, 0x7f, 0xaa, 0x29, 0xa8, 0x06, 0xab, 0x71, 0xa1, 0xa0, 0x5f, 0x00, 0xc2, + 0xc4, 0xf2, 0xcf, 0x49, 0xc8, 0x0c, 0x59, 0x6a, 0x15, 0xbd, 0x07, 0xab, 0xd4, 0x8c, 0x5e, 0x1a, + 0x8e, 0x2d, 0x79, 0x2e, 0xb3, 0xe2, 0x81, 0x8d, 0x0e, 0xa0, 0x3c, 0x32, 0x3d, 0x7b, 0xfc, 0x76, + 0xbe, 0xb3, 0xa2, 0x66, 0xe0, 0xfb, 0xbc, 0x23, 0x96, 0x00, 0xcc, 0xba, 0x33, 0x23, 0x0b, 0x05, + 0xe8, 0x4f, 0x41, 0xeb, 0x53, 0x33, 0xa4, 0x69, 0x76, 0xba, 0x50, 0x64, 0xe3, 0x4b, 0x8b, 0x5e, + 0x64, 0x4c, 0xb1, 0x32, 0x31, 0xef, 0xae, 0xff, 0x7f, 0x01, 0x36, 0x53, 0xd8, 0xd2, 0x52, 0x9f, + 0x40, 0x39, 0x24, 0xd1, 0x64, 0x4c, 0x39, 0x7c, 0x7d, 0xe7, 0x61, 0x4e, 0xf8, 0x19, 0xa4, 0x16, + 0xe6, 0x30, 0x58, 0xc2, 0xa1, 0x6d, 0xd0, 0x44, 0x0f, 0x83, 0x84, 0xa1, 0x1f, 0x1a, 0x6e, 0x34, + 0xe4, 0x52, 0xab, 0xe2, 0xba, 0xa0, 0x77, 0x19, 0xf9, 0x28, 0x1a, 0xa6, 0xa4, 0xaa, 0x5e, 0x53, + 0xaa, 0xc8, 0x04, 0xcd, 0x23, 0xf4, 0xb5, 0x1f, 0xbe, 0x34, 0x98, 0x68, 0x43, 0xc7, 0x26, 0x8d, + 0x22, 0x07, 0xfd, 0x34, 0x27, 0x68, 0x4f, 0x74, 0x3f, 0x96, 0xbd, 0xf1, 0x86, 0x97, 0x25, 0xe8, + 0x3f, 0x84, 0xb2, 0x98, 0x29, 0xb3, 0xa4, 0xfe, 0x69, 0xa7, 0xd3, 0xed, 0xf7, 0xb5, 0x15, 0x54, + 0x85, 0x12, 0xee, 0x0e, 0x30, 0xb3, 0xb0, 0x2a, 0x94, 0x1e, 0xb5, 0x07, 0xed, 0x43, 0xad, 0xa0, + 0xff, 0x00, 0x36, 0x9e, 0x98, 0x0e, 0xcd, 0x63, 0x5c, 0xba, 0x0f, 0xda, 0xb4, 0xad, 0xd4, 0xce, + 0x41, 0x46, 0x3b, 0xf9, 0x45, 0xd3, 0xbd, 0x70, 0xe8, 0x15, 0x7d, 0x68, 0xa0, 0x92, 0x30, 0x94, + 0x2a, 0x60, 0xbf, 0xfa, 0x6b, 0xd8, 0xe8, 0x53, 0x3f, 0xc8, 0x65, 0xf9, 0x3f, 0x82, 0x55, 0xe6, + 0xa3, 0xfc, 0x09, 0x95, 0xa6, 0x7f, 0xbb, 0x25, 0x7c, 0x58, 0x2b, 0xf6, 0x61, 0xad, 0x5d, 0xe9, + 0xe3, 0x70, 0xdc, 0x12, 0xdd, 0x82, 0x72, 0xe4, 0x0c, 0x3d, 0x73, 0x2c, 0x77, 0x0b, 0x59, 0xd2, + 0x11, 0x33, 0xf2, 0x78, 0x60, 0x69, 0xf8, 0x1d, 0x40, 0xbb, 0x24, 0xa2, 0xa1, 0x7f, 0x99, 0x8b, + 0x9f, 0x2d, 0x28, 0x3d, 0xf7, 0x43, 0x4b, 0x2c, 0xc4, 0x0a, 0x16, 0x05, 0xb6, 0xa8, 0x32, 0x20, + 0x12, 0xfb, 0x23, 0x40, 0x07, 0x1e, 0xf3, 0x29, 0xf9, 0x14, 0xf1, 0xf7, 0x05, 0xb8, 0x91, 0x69, + 0x2f, 0x95, 0xb1, 0xfc, 0x3a, 0x64, 0x1b, 0xd3, 0x24, 0x12, 0xeb, 0x10, 0x1d, 0x43, 0x59, 0xb4, + 0x90, 0x92, 0xbc, 0xb7, 0x00, 0x90, 0x70, 0x53, 0x12, 0x4e, 0xc2, 0xcc, 0x35, 0x7a, 0xf5, 0xdd, + 0x1a, 0xfd, 0x6b, 0xd0, 0xe2, 0x79, 0x44, 0x6f, 0xd5, 0xcd, 0xd7, 0x70, 0xc3, 0xf2, 0xc7, 0x63, + 0x62, 0x31, 0x6b, 0x30, 0x1c, 0x8f, 0x92, 0xf0, 0xdc, 0x1c, 0xbf, 0xdd, 0x6e, 0xd0, 0xb4, 0xd7, + 0x81, 0xec, 0xa4, 0x3f, 0x83, 0xcd, 0xd4, 0xc0, 0x52, 0x11, 0x8f, 0xa0, 0x14, 0x31, 0x82, 0xd4, + 0xc4, 0xc7, 0x0b, 0x6a, 0x22, 0xc2, 0xa2, 0xbb, 0x7e, 0x43, 0x80, 0x77, 0xcf, 0x89, 0x97, 0x4c, + 0x4b, 0xdf, 0x85, 0xcd, 0x3e, 0x37, 0xd3, 0x5c, 0x76, 0x38, 0x35, 0xf1, 0x42, 0xc6, 0xc4, 0xb7, + 0x00, 0xa5, 0x51, 0xa4, 0x21, 0x5e, 0xc2, 0x46, 0xf7, 0x82, 0x58, 0xb9, 0x90, 0x1b, 0xb0, 0x6a, + 0xf9, 0xae, 0x6b, 0x7a, 0x76, 0xa3, 0x70, 0x47, 0xdd, 0xae, 0xe2, 0xb8, 0x98, 0x5e, 0x8b, 0x6a, + 0xde, 0xb5, 0xa8, 0xff, 0xad, 0x02, 0xda, 0x74, 0x6c, 0x29, 0x48, 0xc6, 0x3d, 0xb5, 0x19, 0x10, + 0x1b, 0x7b, 0x0d, 0xcb, 0x92, 0xa4, 0xc7, 0xdb, 0x85, 0xa0, 0x93, 0x30, 0x4c, 0x6d, 0x47, 0xea, + 0x35, 0xb7, 0x23, 0x7d, 0x1f, 0x7e, 0x27, 0x66, 0xa7, 0x4f, 0x43, 0x62, 0xba, 0x8e, 0x37, 0x3c, + 0x38, 0x3e, 0x0e, 0x88, 0x60, 0x1c, 0x21, 0x28, 0xda, 0x26, 0x35, 0x25, 0x63, 0xfc, 0x9f, 0x2d, + 0x7a, 0x6b, 0xec, 0x47, 0xc9, 0xa2, 0xe7, 0x05, 0xfd, 0x3f, 0x54, 0x68, 0xcc, 0x40, 0xc5, 0xe2, + 0x7d, 0x06, 0xa5, 0x88, 0xd0, 0x49, 0x20, 0x4d, 0xa5, 0x9b, 0x9b, 0xe1, 0xf9, 0x78, 0xad, 0x3e, + 0x03, 0xc3, 0x02, 0x13, 0x0d, 0xa1, 0x42, 0xe9, 0xa5, 0x11, 0x39, 0x3f, 0x8f, 0x03, 0x82, 0xc3, + 0xeb, 0xe2, 0x0f, 0x48, 0xe8, 0x3a, 0x9e, 0x39, 0xee, 0x3b, 0x3f, 0x27, 0x78, 0x95, 0xd2, 0x4b, + 0xf6, 0x83, 0x9e, 0x32, 0x83, 0xb7, 0x1d, 0x4f, 0x8a, 0xbd, 0xb3, 0xec, 0x28, 0x29, 0x01, 0x63, + 0x81, 0xd8, 0x3c, 0x84, 0x12, 0x9f, 0xd3, 0x32, 0x86, 0xa8, 0x81, 0x4a, 0xe9, 0x25, 0x67, 0xaa, + 0x82, 0xd9, 0x6f, 0xf3, 0x01, 0xac, 0xa5, 0x67, 0xc0, 0x0c, 0x69, 0x44, 0x9c, 0xe1, 0x48, 0x18, + 0x58, 0x09, 0xcb, 0x12, 0xd3, 0xe4, 0x6b, 0xc7, 0x96, 0x21, 0x6b, 0x09, 0x8b, 0x82, 0xfe, 0xaf, + 0x05, 0xb8, 0x3d, 0x47, 0x32, 0xd2, 0x58, 0x9f, 0x65, 0x8c, 0xf5, 0x1d, 0x49, 0x21, 0xb6, 0xf8, + 0x67, 0x19, 0x8b, 0x7f, 0x87, 0xe0, 0x6c, 0xd9, 0xdc, 0x82, 0x32, 0xb9, 0x70, 0x28, 0xb1, 0xa5, + 0xa8, 0x64, 0x29, 0xb5, 0x9c, 0x8a, 0xd7, 0x5d, 0x4e, 0x9f, 0xc0, 0x56, 0x27, 0x24, 0x26, 0x25, + 0x72, 0x2b, 0x8f, 0xed, 0xff, 0x36, 0x54, 0xcc, 0xf1, 0xd8, 0xb7, 0xa6, 0x6a, 0x5d, 0xe5, 0xe5, + 0x03, 0x5b, 0xff, 0x56, 0x81, 0x9b, 0x57, 0xfa, 0x48, 0x49, 0x9f, 0x41, 0xdd, 0x89, 0xfc, 0x31, + 0x9f, 0x84, 0x91, 0x3a, 0xc5, 0xfd, 0x78, 0x31, 0x77, 0x72, 0x10, 0x63, 0xf0, 0x43, 0xdd, 0xba, + 0x93, 0x2e, 0x72, 0xab, 0xe2, 0x83, 0xdb, 0x72, 0x35, 0xc7, 0x45, 0xfd, 0x1f, 0x14, 0xb8, 0x29, + 0xbd, 0x78, 0xee, 0xc9, 0xcc, 0x61, 0xb9, 0xf0, 0xae, 0x59, 0xd6, 0x1b, 0x70, 0xeb, 0x2a, 0x5f, + 0x72, 0x5f, 0xff, 0x27, 0x15, 0xd0, 0xec, 0x09, 0x12, 0x7d, 0x0f, 0xd6, 0x22, 0xe2, 0xd9, 0x86, + 0xf0, 0x09, 0xc2, 0x5d, 0x55, 0x70, 0x8d, 0xd1, 0x84, 0x73, 0x88, 0xd8, 0x36, 0x47, 0x2e, 0x24, + 0xb7, 0x15, 0xcc, 0xff, 0xd1, 0x08, 0xd6, 0x9e, 0x47, 0x46, 0x32, 0x36, 0x37, 0x9a, 0x7a, 0xee, + 0xad, 0x6b, 0x96, 0x8f, 0xd6, 0xa3, 0x7e, 0x32, 0x2f, 0x5c, 0x7b, 0x1e, 0x25, 0x05, 0xf4, 0x4b, + 0x05, 0xde, 0x8b, 0x43, 0x87, 0xa9, 0xf8, 0x5c, 0xdf, 0x26, 0x51, 0xa3, 0x78, 0x47, 0xdd, 0xae, + 0xef, 0x9c, 0x5c, 0x43, 0x7e, 0x33, 0xc4, 0x23, 0xdf, 0x26, 0xf8, 0xa6, 0x37, 0x87, 0x1a, 0xa1, + 0x16, 0xdc, 0x70, 0x27, 0x11, 0x35, 0x84, 0x15, 0x18, 0xb2, 0x51, 0xa3, 0xc4, 0xe5, 0xb2, 0xc9, + 0xaa, 0x32, 0xb6, 0xaa, 0xb7, 0xa0, 0x96, 0x9a, 0x16, 0xaa, 0x40, 0xb1, 0x77, 0xdc, 0xeb, 0x6a, + 0x2b, 0x08, 0xa0, 0xdc, 0xd9, 0xc7, 0xc7, 0xc7, 0x03, 0x11, 0x89, 0x1f, 0x1c, 0xb5, 0xf7, 0xba, + 0x5a, 0x41, 0xff, 0xdf, 0x02, 0x6c, 0xcd, 0x63, 0x12, 0xd9, 0x50, 0x64, 0x13, 0x96, 0xc7, 0x9f, + 0x77, 0x3f, 0x5f, 0x8e, 0xce, 0xf4, 0x1c, 0x98, 0x72, 0xbf, 0xab, 0x62, 0xfe, 0x8f, 0x0c, 0x28, + 0x8f, 0xcd, 0x33, 0x32, 0x8e, 0x1a, 0x2a, 0xbf, 0x20, 0xd8, 0xbb, 0xce, 0xd8, 0x87, 0x1c, 0x49, + 0xdc, 0x0e, 0x48, 0xd8, 0xe6, 0x7d, 0xa8, 0xa5, 0xc8, 0x73, 0x8e, 0xe1, 0x5b, 0xe9, 0x63, 0x78, + 0x35, 0x7d, 0xa6, 0x7e, 0x38, 0x2b, 0x2d, 0x36, 0x1b, 0x26, 0xe7, 0xfd, 0xe3, 0xfe, 0x40, 0x1c, + 0x78, 0xf6, 0xf0, 0xf1, 0xe9, 0x89, 0xa6, 0x30, 0xe2, 0xa0, 0xdd, 0x7f, 0xac, 0x15, 0x12, 0x35, + 0xa8, 0xfa, 0x33, 0xa8, 0xee, 0xf6, 0xfa, 0xe2, 0x00, 0xca, 0x16, 0x7b, 0x44, 0x42, 0x36, 0x05, + 0x7e, 0x17, 0x52, 0xc5, 0x71, 0x11, 0x35, 0xa1, 0x12, 0x11, 0x33, 0xb4, 0x46, 0x24, 0x92, 0xde, + 0x25, 0x29, 0xb3, 0x5e, 0x3e, 0xbf, 0x53, 0x10, 0x02, 0xaa, 0xe2, 0xb8, 0xa8, 0xff, 0xdf, 0x2a, + 0xc0, 0xf4, 0x7c, 0x8b, 0xea, 0x50, 0x48, 0x76, 0x84, 0x82, 0x63, 0x33, 0x61, 0x7b, 0xa6, 0x1b, + 0xcf, 0x8a, 0xff, 0xa3, 0x1d, 0xb8, 0xe9, 0x46, 0xc3, 0xc0, 0xb4, 0x5e, 0x1a, 0xf2, 0x58, 0x6a, + 0xf1, 0xce, 0x7c, 0x75, 0xad, 0xe1, 0x1b, 0xb2, 0x52, 0xae, 0x1e, 0x81, 0x7b, 0x08, 0x2a, 0xf1, + 0xce, 0xf9, 0x4a, 0xa8, 0xed, 0x7c, 0xbe, 0xf0, 0xb9, 0xbb, 0xd5, 0xf5, 0xce, 0x85, 0x42, 0x18, + 0x0c, 0x32, 0x00, 0x6c, 0x72, 0xee, 0x58, 0xc4, 0x60, 0xa0, 0x25, 0x0e, 0xfa, 0xe5, 0xe2, 0xa0, + 0xbb, 0x1c, 0x23, 0x81, 0xae, 0xda, 0x71, 0x19, 0xf5, 0xa0, 0x1a, 0x92, 0xc8, 0x9f, 0x84, 0x16, + 0x89, 0x1a, 0xe5, 0x85, 0x42, 0x63, 0x1c, 0xf7, 0xc3, 0x53, 0x08, 0xb4, 0x0b, 0x65, 0xd7, 0x9f, + 0x78, 0x34, 0x6a, 0xac, 0x72, 0x66, 0x3f, 0xcc, 0x09, 0x76, 0xc4, 0x3a, 0x61, 0xd9, 0x17, 0xed, + 0xc1, 0xaa, 0x60, 0x31, 0x6a, 0x54, 0x38, 0xcc, 0x47, 0x79, 0x37, 0x32, 0xde, 0x0b, 0xc7, 0xbd, + 0x99, 0x56, 0x27, 0x11, 0x09, 0x1b, 0x55, 0xa1, 0x55, 0xf6, 0x8f, 0xde, 0x87, 0xaa, 0xf0, 0x08, + 0xb6, 0x13, 0x36, 0x80, 0x57, 0x08, 0x17, 0xb1, 0xeb, 0x84, 0xe8, 0x03, 0xa8, 0x09, 0xef, 0x6e, + 0xf0, 0xa5, 0x57, 0xe3, 0xd5, 0x20, 0x48, 0x27, 0x6c, 0x01, 0x8a, 0x06, 0x24, 0x0c, 0x45, 0x83, + 0xb5, 0xa4, 0x01, 0x09, 0x43, 0xde, 0xe0, 0xf7, 0x61, 0x83, 0xc7, 0x44, 0xc3, 0xd0, 0x9f, 0x04, + 0x06, 0xb7, 0xa9, 0x75, 0xde, 0x68, 0x9d, 0x91, 0xf7, 0x18, 0xb5, 0xc7, 0x8c, 0xeb, 0x36, 0x54, + 0x5e, 0xf8, 0x67, 0xa2, 0x41, 0x5d, 0x38, 0xa6, 0x17, 0xfe, 0x59, 0x5c, 0x95, 0xf8, 0xac, 0x8d, + 0xac, 0xcf, 0x7a, 0x05, 0xb7, 0x66, 0x37, 0x5f, 0xee, 0xbb, 0xb4, 0xeb, 0xfb, 0xae, 0x2d, 0x6f, + 0xde, 0x66, 0xf7, 0x15, 0xa8, 0xb6, 0x17, 0x35, 0x36, 0x17, 0x32, 0x8e, 0x64, 0x1d, 0x63, 0xd6, + 0xb9, 0xf9, 0x29, 0x54, 0x62, 0xeb, 0x5b, 0x64, 0x4b, 0x69, 0x3e, 0x80, 0x7a, 0xd6, 0x76, 0x17, + 0xda, 0x90, 0xfe, 0x53, 0x81, 0x6a, 0x62, 0xa5, 0xc8, 0x83, 0x1b, 0x5c, 0x8a, 0x2c, 0x60, 0x30, + 0xa6, 0x46, 0x2f, 0xc2, 0x94, 0x2f, 0x72, 0xce, 0xab, 0x1d, 0x23, 0xc8, 0x33, 0x91, 0x5c, 0x01, + 0x28, 0x41, 0x9e, 0x8e, 0xf7, 0x0d, 0x6c, 0x8c, 0x1d, 0x6f, 0x72, 0x91, 0x1a, 0x4b, 0xc4, 0x17, + 0x7f, 0x90, 0x73, 0xac, 0x43, 0xd6, 0x7b, 0x3a, 0x46, 0x7d, 0x9c, 0x29, 0xeb, 0xdf, 0x16, 0xe0, + 0xd6, 0x7c, 0x76, 0x50, 0x0f, 0x54, 0x2b, 0x98, 0xc8, 0xa9, 0x3d, 0x58, 0x74, 0x6a, 0x9d, 0x60, + 0x32, 0x1d, 0x95, 0x01, 0xa1, 0x27, 0x50, 0x76, 0x89, 0xeb, 0x87, 0x97, 0x72, 0x06, 0x0f, 0x17, + 0x85, 0x3c, 0xe2, 0xbd, 0xa7, 0xa8, 0x12, 0x0e, 0x61, 0xa8, 0x48, 0x9b, 0x8b, 0xe4, 0xee, 0xb6, + 0xe0, 0xf5, 0x43, 0x0c, 0x89, 0x13, 0x1c, 0xfd, 0x53, 0xb8, 0x39, 0x77, 0x2a, 0xe8, 0x77, 0x01, + 0xac, 0x60, 0x62, 0xf0, 0xeb, 0x61, 0xa1, 0x77, 0x15, 0x57, 0xad, 0x60, 0xd2, 0xe7, 0x04, 0xfd, + 0x1e, 0x34, 0xde, 0xc4, 0x2f, 0xdb, 0x33, 0x04, 0xc7, 0x86, 0x7b, 0xc6, 0x65, 0xa0, 0xe2, 0x8a, + 0x20, 0x1c, 0x9d, 0xe9, 0xbf, 0x2a, 0xc0, 0xc6, 0x15, 0x76, 0x58, 0xf8, 0x2e, 0xf6, 0xa0, 0xf8, + 0x60, 0x24, 0x4a, 0x6c, 0x43, 0xb2, 0x1c, 0x3b, 0xbe, 0x52, 0xe3, 0xff, 0xdc, 0x15, 0x05, 0xf2, + 0xba, 0xab, 0xe0, 0x04, 0xcc, 0xa0, 0xdd, 0x33, 0x87, 0x46, 0x3c, 0xc2, 0x2f, 0x61, 0x51, 0x40, + 0x4f, 0xa1, 0x1e, 0x12, 0xee, 0x02, 0x6d, 0x23, 0xf0, 0x43, 0x1a, 0x0b, 0x6c, 0x67, 0x31, 0x81, + 0x9d, 0xf8, 0x21, 0xc5, 0xeb, 0x31, 0x12, 0x2b, 0x45, 0xe8, 0x09, 0xac, 0xdb, 0x97, 0x9e, 0xe9, + 0x3a, 0x96, 0x44, 0x2e, 0x2f, 0x8d, 0xbc, 0x26, 0x81, 0x38, 0xb0, 0x7e, 0x1f, 0x6a, 0xa9, 0x4a, + 0x36, 0x31, 0x1e, 0x65, 0x48, 0x99, 0x88, 0x42, 0x76, 0xfd, 0x96, 0xe4, 0xfa, 0xd5, 0xff, 0xb9, + 0x00, 0xf5, 0xec, 0x02, 0x88, 0xf5, 0x17, 0x90, 0xd0, 0xf1, 0xed, 0x94, 0xfe, 0x4e, 0x38, 0x81, + 0xe9, 0x88, 0x55, 0xbf, 0x9a, 0xf8, 0xd4, 0x8c, 0x75, 0x64, 0x05, 0x93, 0x3f, 0x64, 0xe5, 0x2b, + 0xba, 0x57, 0xaf, 0xe8, 0x1e, 0x7d, 0x08, 0x48, 0xea, 0x77, 0xec, 0xb8, 0x0e, 0x35, 0xce, 0x2e, + 0x29, 0x11, 0xf2, 0x57, 0xb1, 0x26, 0x6a, 0x0e, 0x59, 0xc5, 0x57, 0x8c, 0x8e, 0x74, 0x58, 0xf7, + 0x7d, 0xd7, 0x88, 0x2c, 0x3f, 0x24, 0x86, 0x69, 0xbf, 0xe0, 0x11, 0xa7, 0x8a, 0x6b, 0xbe, 0xef, + 0xf6, 0x19, 0xad, 0x6d, 0xbf, 0x60, 0x7e, 0xc2, 0x0a, 0x26, 0x11, 0xa1, 0x06, 0xfb, 0x70, 0xd7, + 0x5a, 0xc5, 0x20, 0x48, 0x9d, 0x60, 0x12, 0xa5, 0x1a, 0xb8, 0xc4, 0x65, 0xee, 0x32, 0xd5, 0xe0, + 0x88, 0xb8, 0x6c, 0x94, 0xb5, 0x13, 0x12, 0x5a, 0xc4, 0xa3, 0x03, 0xc7, 0x7a, 0xc9, 0x3c, 0xa1, + 0xb2, 0xad, 0xe0, 0xb5, 0x20, 0x45, 0xd3, 0x7f, 0x06, 0x25, 0xee, 0x39, 0xd9, 0xe4, 0xb9, 0xd7, + 0xe1, 0x4e, 0x49, 0x88, 0xb7, 0xc2, 0x08, 0xdc, 0x25, 0xbd, 0x0f, 0xd5, 0x91, 0x1f, 0x49, 0x97, + 0x26, 0x2c, 0xaf, 0xc2, 0x08, 0xbc, 0xb2, 0x09, 0x95, 0x90, 0x98, 0xb6, 0xef, 0x8d, 0xe3, 0x53, + 0x79, 0x52, 0xd6, 0x5f, 0x41, 0x59, 0x6c, 0xbf, 0xd7, 0xc0, 0xff, 0x08, 0x90, 0x25, 0x7c, 0x61, + 0xc0, 0x4e, 0xf9, 0x51, 0x24, 0x83, 0x33, 0xfe, 0x14, 0x24, 0x6a, 0x4e, 0xa6, 0x15, 0xfa, 0x7f, + 0x29, 0x22, 0x4c, 0x13, 0x97, 0xf4, 0x2c, 0x9e, 0x63, 0x96, 0xc6, 0x8e, 0x34, 0xe2, 0x36, 0x20, + 0x2e, 0xb2, 0x83, 0xb0, 0x8c, 0xc6, 0x0a, 0xcb, 0xbe, 0x71, 0x48, 0x80, 0xf8, 0x6e, 0x90, 0xc8, + 0x53, 0xd3, 0xa2, 0x77, 0x83, 0x44, 0xdc, 0x0d, 0x12, 0x76, 0x76, 0x93, 0x71, 0xa2, 0x80, 0x2b, + 0xf2, 0x30, 0xb1, 0x66, 0x27, 0x17, 0xb0, 0x44, 0xff, 0x1f, 0x25, 0xd9, 0x2b, 0xe2, 0x8b, 0x52, + 0xf4, 0x0d, 0x54, 0xd8, 0xb2, 0x33, 0x5c, 0x33, 0x90, 0xcf, 0x7e, 0x9d, 0xe5, 0xee, 0x60, 0x5b, + 0x6c, 0x95, 0x1d, 0x99, 0x81, 0x88, 0xf2, 0x56, 0x03, 0x51, 0x62, 0x7b, 0x8e, 0x69, 0x4f, 0xf7, + 0x1c, 0xf6, 0x8f, 0xbe, 0x0f, 0x75, 0x73, 0x42, 0x7d, 0xc3, 0xb4, 0xcf, 0x49, 0x48, 0x9d, 0x88, + 0x48, 0xdd, 0xaf, 0x33, 0x6a, 0x3b, 0x26, 0x36, 0x3f, 0x87, 0xb5, 0x34, 0xe6, 0xdb, 0xbc, 0x6f, + 0x29, 0xed, 0x7d, 0xff, 0x04, 0x60, 0x7a, 0xe9, 0xc0, 0x6c, 0x84, 0x5c, 0x38, 0xd4, 0xb0, 0xe2, + 0x73, 0x53, 0x09, 0x57, 0x18, 0xa1, 0xc3, 0x4e, 0x08, 0xd9, 0x1b, 0xd1, 0x52, 0x7c, 0x23, 0xca, + 0x56, 0x2d, 0x5b, 0x68, 0x2f, 0x9d, 0xf1, 0x38, 0xb9, 0x08, 0xa9, 0xfa, 0xbe, 0xfb, 0x98, 0x13, + 0xf4, 0xdf, 0x14, 0x84, 0xad, 0x88, 0xbb, 0xed, 0x5c, 0x21, 0xfd, 0xbb, 0x52, 0xf5, 0x7d, 0x80, + 0x88, 0x9a, 0x21, 0x0b, 0x25, 0xcc, 0xf8, 0x2a, 0xa6, 0x39, 0x73, 0xa5, 0x3a, 0x88, 0x9f, 0xe8, + 0x71, 0x55, 0xb6, 0x6e, 0x53, 0xf4, 0x05, 0xac, 0x59, 0xbe, 0x1b, 0x8c, 0x89, 0xec, 0x5c, 0x7a, + 0x6b, 0xe7, 0x5a, 0xd2, 0xbe, 0x4d, 0x53, 0x17, 0x40, 0xe5, 0xeb, 0x5e, 0x00, 0xfd, 0x9b, 0x22, + 0xae, 0xe8, 0xd3, 0x2f, 0x04, 0x68, 0x38, 0xe7, 0x19, 0x7a, 0x6f, 0xc9, 0xe7, 0x86, 0xef, 0x7a, + 0x83, 0x6e, 0x7e, 0x91, 0xe7, 0xd1, 0xf7, 0xcd, 0xc1, 0xdd, 0xbf, 0xab, 0x50, 0x4d, 0x6e, 0xe7, + 0x67, 0x74, 0xff, 0x19, 0x54, 0x93, 0xfc, 0x08, 0xb9, 0x41, 0x7c, 0xa7, 0x7a, 0x92, 0xc6, 0xe8, + 0x39, 0x20, 0x73, 0x38, 0x4c, 0x82, 0x36, 0x63, 0x12, 0x99, 0xc3, 0xf8, 0x6d, 0xe4, 0xb3, 0x05, + 0xe4, 0x10, 0xfb, 0xad, 0x53, 0xd6, 0x1f, 0x6b, 0xe6, 0x70, 0x98, 0xa1, 0xa0, 0x3f, 0x85, 0x9b, + 0xd9, 0x31, 0x8c, 0xb3, 0x4b, 0x23, 0x70, 0x6c, 0x79, 0x74, 0xdc, 0x5f, 0xf4, 0x81, 0xa2, 0x95, + 0x81, 0xff, 0xea, 0xf2, 0xc4, 0xb1, 0x85, 0xcc, 0x51, 0x38, 0x53, 0xd1, 0xfc, 0x73, 0x78, 0xef, + 0x0d, 0xcd, 0xe7, 0xe8, 0xa0, 0x97, 0x7d, 0x78, 0x5f, 0x5e, 0x08, 0x29, 0xed, 0xfd, 0x5a, 0x11, + 0xef, 0x28, 0x59, 0x99, 0xb4, 0xd3, 0x71, 0xeb, 0xdd, 0x9c, 0xe3, 0x74, 0x4e, 0x4e, 0x05, 0x3c, + 0x0f, 0x55, 0xbf, 0xbe, 0x12, 0xaa, 0xe6, 0x0d, 0x62, 0x44, 0xc4, 0x27, 0x80, 0x24, 0x82, 0xfe, + 0x2f, 0x2a, 0x54, 0x62, 0x74, 0x7e, 0xf0, 0xbb, 0x8c, 0x28, 0x71, 0x8d, 0xe4, 0xea, 0x47, 0xc1, + 0x20, 0x48, 0xfc, 0x9a, 0xe3, 0x7d, 0xa8, 0xb2, 0xf3, 0xa5, 0xa8, 0x2e, 0xf0, 0xea, 0x0a, 0x23, + 0xf0, 0xca, 0x0f, 0xa0, 0x46, 0x7d, 0x6a, 0x8e, 0x0d, 0xca, 0x7d, 0xb9, 0x2a, 0x7a, 0x73, 0x12, + 0xf7, 0xe4, 0xe8, 0x87, 0xb0, 0x49, 0x47, 0xa1, 0x4f, 0xe9, 0x98, 0xc5, 0x77, 0x3c, 0xa2, 0x11, + 0x01, 0x48, 0x11, 0x6b, 0x49, 0x85, 0x88, 0x74, 0x22, 0xb6, 0x7b, 0x4f, 0x1b, 0x33, 0xd3, 0xe5, + 0x9b, 0x48, 0x11, 0xaf, 0x27, 0x54, 0x66, 0xda, 0xcc, 0x79, 0xca, 0x68, 0x81, 0xef, 0x15, 0x0a, + 0x8e, 0x8b, 0xc8, 0x80, 0x0d, 0x97, 0x98, 0xd1, 0x24, 0x24, 0xb6, 0xf1, 0xdc, 0x21, 0x63, 0x5b, + 0x9c, 0xd7, 0xeb, 0xb9, 0xc3, 0xef, 0x58, 0x2c, 0xad, 0x47, 0xbc, 0x37, 0xae, 0xc7, 0x70, 0xa2, + 0xcc, 0x22, 0x07, 0xf1, 0x87, 0x36, 0xa0, 0xd6, 0x7f, 0xda, 0x1f, 0x74, 0x8f, 0x8c, 0xa3, 0xe3, + 0xdd, 0xae, 0xcc, 0xad, 0xe8, 0x77, 0xb1, 0x28, 0x2a, 0xac, 0x7e, 0x70, 0x3c, 0x68, 0x1f, 0x1a, + 0x83, 0x83, 0xce, 0xe3, 0xbe, 0x56, 0x40, 0x37, 0x61, 0x73, 0xb0, 0x8f, 0x8f, 0x07, 0x83, 0xc3, + 0xee, 0xae, 0x71, 0xd2, 0xc5, 0x07, 0xc7, 0xbb, 0x7d, 0x4d, 0x45, 0x08, 0xea, 0x53, 0xf2, 0xe0, + 0xe0, 0xa8, 0xab, 0x15, 0x51, 0x0d, 0x56, 0x4f, 0xba, 0xb8, 0xd3, 0xed, 0x0d, 0xb4, 0x92, 0xfe, + 0x2b, 0x15, 0x6a, 0x29, 0x2d, 0x32, 0x43, 0x0e, 0x23, 0x11, 0xe7, 0x17, 0x31, 0xfb, 0xe5, 0x6f, + 0x41, 0xa6, 0x35, 0x12, 0xda, 0x29, 0x62, 0x51, 0xe0, 0xb1, 0xbd, 0x79, 0x91, 0x5a, 0xe7, 0x45, + 0x5c, 0x71, 0xcd, 0x0b, 0x01, 0xf2, 0x3d, 0x58, 0x7b, 0x49, 0x42, 0x8f, 0x8c, 0x65, 0xbd, 0xd0, + 0x48, 0x4d, 0xd0, 0x44, 0x93, 0x6d, 0xd0, 0x64, 0x93, 0x29, 0x8c, 0x50, 0x47, 0x5d, 0xd0, 0x8f, + 0x62, 0xb0, 0x2d, 0x28, 0x89, 0xea, 0x55, 0x31, 0x3e, 0x2f, 0x30, 0x37, 0x15, 0xbd, 0x36, 0x03, + 0x1e, 0xdf, 0x15, 0x31, 0xff, 0x47, 0x67, 0xb3, 0xfa, 0x29, 0x73, 0xfd, 0xdc, 0x5f, 0xdc, 0x9c, + 0xdf, 0xa4, 0xa2, 0x51, 0xa2, 0xa2, 0x55, 0x50, 0x71, 0x9c, 0x90, 0xd0, 0x69, 0x77, 0xf6, 0x99, + 0x5a, 0xd6, 0xa1, 0x7a, 0xd4, 0xfe, 0xa9, 0x71, 0xda, 0xe7, 0x57, 0xa1, 0x48, 0x83, 0xb5, 0xc7, + 0x5d, 0xdc, 0xeb, 0x1e, 0x4a, 0x8a, 0x8a, 0xb6, 0x40, 0x93, 0x94, 0x69, 0xbb, 0x22, 0x43, 0x10, + 0xbf, 0x25, 0x54, 0x81, 0x62, 0xff, 0x49, 0xfb, 0x44, 0x2b, 0xeb, 0xff, 0x5d, 0x80, 0x0d, 0xe1, + 0x16, 0x92, 0xa7, 0xd3, 0x37, 0x3f, 0x1d, 0xa5, 0x2f, 0x3f, 0x0a, 0xd9, 0xcb, 0x8f, 0x38, 0x08, + 0xe5, 0x5e, 0x5d, 0x9d, 0x06, 0xa1, 0xfc, 0xd2, 0x24, 0xb3, 0xe3, 0x17, 0x17, 0xd9, 0xf1, 0x1b, + 0xb0, 0xea, 0x92, 0x28, 0xd1, 0x5b, 0x15, 0xc7, 0x45, 0xe4, 0x40, 0xcd, 0xf4, 0x3c, 0x9f, 0x9a, + 0xe2, 0x46, 0xb1, 0xbc, 0x90, 0x33, 0xbc, 0x32, 0xe3, 0x56, 0x7b, 0x8a, 0x24, 0x36, 0xe6, 0x34, + 0x76, 0xf3, 0x27, 0xa0, 0x5d, 0x6d, 0xb0, 0x88, 0x3b, 0xfc, 0xc1, 0x27, 0x53, 0x6f, 0x48, 0xd8, + 0xba, 0x38, 0xed, 0x3d, 0xee, 0x1d, 0x3f, 0xe9, 0x69, 0x2b, 0xac, 0x80, 0x4f, 0x7b, 0xbd, 0x83, + 0xde, 0x9e, 0xa6, 0x20, 0x80, 0x72, 0xf7, 0xa7, 0x07, 0x83, 0xee, 0xae, 0x56, 0xd8, 0xf9, 0xf5, + 0x26, 0x94, 0x05, 0x93, 0xe8, 0x5b, 0x19, 0x09, 0xa4, 0xd3, 0xf2, 0xd0, 0x4f, 0x16, 0x8e, 0xa8, + 0x33, 0xa9, 0x7e, 0xcd, 0x87, 0x4b, 0xf7, 0x97, 0x4f, 0x24, 0x2b, 0xe8, 0xaf, 0x15, 0x58, 0xcb, + 0x3c, 0x8f, 0xe4, 0xbd, 0x51, 0x9d, 0x93, 0x05, 0xd8, 0xfc, 0xf1, 0x52, 0x7d, 0x13, 0x5e, 0x7e, + 0xa9, 0x40, 0x2d, 0x95, 0xff, 0x86, 0xee, 0x2f, 0x93, 0x33, 0x27, 0x38, 0xf9, 0x7c, 0xf9, 0x74, + 0x3b, 0x7d, 0xe5, 0x63, 0x05, 0xfd, 0x95, 0x02, 0xb5, 0x54, 0x26, 0x58, 0x6e, 0x56, 0x66, 0xf3, + 0xd6, 0x72, 0xb3, 0x32, 0x2f, 0xf1, 0x6c, 0x05, 0xfd, 0x85, 0x02, 0xd5, 0x24, 0xab, 0x0b, 0xdd, + 0x5b, 0x3c, 0x0f, 0x4c, 0x30, 0xf1, 0xd9, 0xb2, 0x09, 0x64, 0xfa, 0x0a, 0xfa, 0x33, 0xa8, 0xc4, + 0x29, 0x50, 0x28, 0xaf, 0xf7, 0xba, 0x92, 0x5f, 0xd5, 0xbc, 0xb7, 0x70, 0xbf, 0xf4, 0xf0, 0x71, + 0x5e, 0x52, 0xee, 0xe1, 0xaf, 0x64, 0x50, 0x35, 0xef, 0x2d, 0xdc, 0x2f, 0x19, 0x9e, 0x59, 0x42, + 0x2a, 0x7d, 0x29, 0xb7, 0x25, 0xcc, 0xe6, 0x4d, 0xe5, 0xb6, 0x84, 0x79, 0xd9, 0x52, 0x82, 0x91, + 0x54, 0x02, 0x54, 0x6e, 0x46, 0x66, 0x93, 0xac, 0x72, 0x33, 0x32, 0x27, 0xdf, 0x4a, 0x5f, 0x41, + 0xbf, 0x50, 0xd2, 0xe7, 0x82, 0x7b, 0x0b, 0xe7, 0xf9, 0x2c, 0x68, 0x92, 0x33, 0x99, 0x46, 0x7c, + 0x81, 0xfe, 0x42, 0xde, 0x62, 0x88, 0x34, 0x21, 0xb4, 0x08, 0x58, 0x26, 0xb3, 0xa8, 0xf9, 0xe9, + 0x72, 0xce, 0x86, 0x33, 0xf1, 0x97, 0x0a, 0xc0, 0x34, 0xa1, 0x28, 0x37, 0x13, 0x33, 0x99, 0x4c, + 0xcd, 0xfb, 0x4b, 0xf4, 0x4c, 0x2f, 0x90, 0x38, 0xe1, 0x21, 0xf7, 0x02, 0xb9, 0x92, 0xf0, 0x94, + 0x7b, 0x81, 0x5c, 0x4d, 0x56, 0xd2, 0x57, 0xd0, 0x3f, 0x2a, 0xb0, 0x39, 0x93, 0x70, 0x81, 0x1e, + 0x5e, 0x33, 0xe7, 0xa6, 0xf9, 0xe5, 0xf2, 0x00, 0x31, 0x6b, 0xdb, 0xca, 0xc7, 0x0a, 0xfa, 0x1b, + 0x05, 0xd6, 0x33, 0x8f, 0xd4, 0x28, 0xb7, 0x97, 0x9a, 0x93, 0xba, 0xd1, 0x7c, 0xb0, 0x5c, 0xe7, + 0x44, 0x5a, 0x7f, 0xa7, 0x40, 0x3d, 0x9b, 0xaf, 0x80, 0x1e, 0x2c, 0xb6, 0x2d, 0x5c, 0x61, 0xe8, + 0x8b, 0x25, 0x7b, 0xc7, 0x1c, 0x7d, 0xb5, 0xfa, 0x47, 0x25, 0x11, 0xbd, 0x95, 0xf9, 0xe7, 0x47, + 0xbf, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x1c, 0x14, 0x37, 0x01, 0x73, 0x31, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/plugins/drivers/proto/driver.proto b/plugins/drivers/proto/driver.proto index 653fe348663..76a48526543 100644 --- a/plugins/drivers/proto/driver.proto +++ b/plugins/drivers/proto/driver.proto @@ -385,6 +385,12 @@ message NetworkIsolationSpec { map labels = 3; } +message DNSConfig { + repeated string servers = 1; + repeated string searches = 2; + repeated string options = 3; +} + message TaskConfig { // Id of the task, recommended to the globally unique, must be unique to the driver. @@ -440,6 +446,9 @@ message TaskConfig { // NetworkIsolationSpec specifies the configuration for the network namespace // to use for the task. *Only supported on Linux NetworkIsolationSpec network_isolation_spec = 16; + + // DNSConfig is the configuration for task DNS resolvers and other options + DNSConfig dns = 17; } message Resources { diff --git a/plugins/drivers/utils.go b/plugins/drivers/utils.go index c146e96ef80..bfb3d5c2614 100644 --- a/plugins/drivers/utils.go +++ b/plugins/drivers/utils.go @@ -65,6 +65,7 @@ func taskConfigFromProto(pb *proto.TaskConfig) *TaskConfig { StderrPath: pb.StderrPath, AllocID: pb.AllocId, NetworkIsolation: NetworkIsolationSpecFromProto(pb.NetworkIsolationSpec), + DNS: dnsConfigFromProto(pb.Dns), } } @@ -89,6 +90,7 @@ func taskConfigToProto(cfg *TaskConfig) *proto.TaskConfig { StderrPath: cfg.StderrPath, AllocId: cfg.AllocID, NetworkIsolationSpec: NetworkIsolationSpecToProto(cfg.NetworkIsolation), + Dns: dnsConfigToProto(cfg.DNS), } return pb } @@ -625,3 +627,27 @@ func NetworkIsolationSpecFromProto(pb *proto.NetworkIsolationSpec) *NetworkIsola Mode: netIsolationModeFromProto(pb.Mode), } } + +func dnsConfigToProto(dns *DNSConfig) *proto.DNSConfig { + if dns == nil { + return nil + } + + return &proto.DNSConfig{ + Servers: dns.Servers, + Searches: dns.Searches, + Options: dns.Options, + } +} + +func dnsConfigFromProto(pb *proto.DNSConfig) *DNSConfig { + if pb == nil { + return nil + } + + return &DNSConfig{ + Servers: pb.Servers, + Searches: pb.Searches, + Options: pb.Options, + } +} From 96247dde30e010d15d08dec30af29d588680cb15 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Thu, 9 Apr 2020 13:20:18 -0400 Subject: [PATCH 02/18] drivers: build resolv.conf and bind into executor for exec/java drivers --- drivers/exec/driver.go | 9 +++ drivers/java/driver.go | 9 +++ drivers/qemu/driver.go | 13 +++- drivers/shared/resolvconf/mount.go | 72 ++++++++++++++++++++ drivers/shared/resolvconf/resolvconf.go | 45 ++++++++++++ drivers/shared/resolvconf/resolvconf_test.go | 52 ++++++++++++++ 6 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 drivers/shared/resolvconf/mount.go create mode 100644 drivers/shared/resolvconf/resolvconf.go create mode 100644 drivers/shared/resolvconf/resolvconf_test.go diff --git a/drivers/exec/driver.go b/drivers/exec/driver.go index 8d8b46e23b6..2a3cf4dba1c 100644 --- a/drivers/exec/driver.go +++ b/drivers/exec/driver.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/drivers/shared/eventer" "github.com/hashicorp/nomad/drivers/shared/executor" + "github.com/hashicorp/nomad/drivers/shared/resolvconf" "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/helper/pluginutils/loader" "github.com/hashicorp/nomad/plugins/base" @@ -369,6 +370,14 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive user = "nobody" } + if cfg.DNS != nil { + dnsMount, err := resolvconf.GenerateDNSMount(cfg.TaskDir().Dir, cfg.DNS) + if err != nil { + return nil, nil, fmt.Errorf("failed to build mount for resolv.conf: %v", err) + } + cfg.Mounts = append(cfg.Mounts, dnsMount) + } + execCmd := &executor.ExecCommand{ Cmd: driverConfig.Command, Args: driverConfig.Args, diff --git a/drivers/java/driver.go b/drivers/java/driver.go index 02931cdcf4c..44ef04fa5dc 100644 --- a/drivers/java/driver.go +++ b/drivers/java/driver.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/nomad/client/fingerprint" "github.com/hashicorp/nomad/drivers/shared/eventer" "github.com/hashicorp/nomad/drivers/shared/executor" + "github.com/hashicorp/nomad/drivers/shared/resolvconf" "github.com/hashicorp/nomad/helper/pluginutils/loader" "github.com/hashicorp/nomad/plugins/base" "github.com/hashicorp/nomad/plugins/drivers" @@ -344,6 +345,14 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive user = "nobody" } + if cfg.DNS != nil { + dnsMount, err := resolvconf.GenerateDNSMount(cfg.TaskDir().Dir, cfg.DNS) + if err != nil { + return nil, nil, fmt.Errorf("failed to build mount for resolv.conf: %v", err) + } + cfg.Mounts = append(cfg.Mounts, dnsMount) + } + execCmd := &executor.ExecCommand{ Cmd: absPath, Args: args, diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index f24bda73d66..e119e94c6bf 100644 --- a/drivers/qemu/driver.go +++ b/drivers/qemu/driver.go @@ -346,6 +346,17 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive "-nographic", } + var dnsArgs string + if cfg.DNS != nil { + if len(cfg.DNS.Servers) > 0 { + dnsArgs = ",dns=" + cfg.DNS.Servers[0] + } + + for _, s := range cfg.DNS.Searches { + dnsArgs = fmt.Sprintf("%s,dnssearch=%s", dnsArgs, s) + } + } + var monitorPath string if driverConfig.GracefulShutdown { if runtime.GOOS == "windows" { @@ -400,7 +411,7 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive if len(forwarding) != 0 { args = append(args, "-netdev", - fmt.Sprintf("user,id=user.0,%s", strings.Join(forwarding, ",")), + fmt.Sprintf("user,id=user.0,%s%s", strings.Join(forwarding, ","), dnsArgs), "-device", "virtio-net,netdev=user.0", ) } diff --git a/drivers/shared/resolvconf/mount.go b/drivers/shared/resolvconf/mount.go new file mode 100644 index 00000000000..0147e94ee9e --- /dev/null +++ b/drivers/shared/resolvconf/mount.go @@ -0,0 +1,72 @@ +package resolvconf + +import ( + "io" + "os" + "path/filepath" + + "github.com/hashicorp/nomad/plugins/drivers" +) + +func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountConfig, error) { + var nSearches, nServers, nOptions int + path := filepath.Join(taskDir, "resolv.conf") + if conf != nil { + nServers := len(conf.Servers) + nSearches := len(conf.Searches) + nOptions := len(conf.Options) + } + + // Use system dns if no configuration is given + if nServers == 0 && nSearches == 0 && nOptions == 0 { + if err := copySystemDNS(path); err != nil { + return nil, err + } + + return &drivers.MountConfig{ + TaskPath: "/etc/resolv.conf", + HostPath: dest, + Readonly: true, + PropagationMode: "private", + }, nil + } + + f, err := os.Create(path) + if err != nil { + return nil, err + } + defer f.Close() + + rc, err := New(conf.Servers, conf.Searches, conf.Options) + if err != nil { + return nil, err + } + + if _, err := f.Write(rc.Content()); err != nil { + return nil, err + } + + return &drivers.MountConfig{ + TaskPath: "/etc/resolv.conf", + HostPath: path, + Readonly: true, + PropagationMode: "private", + }, nil +} + +func copySystemDNS(dest string) error { + in, err := os.Open("/etc/resolv.conf") + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dest) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + return err +} diff --git a/drivers/shared/resolvconf/resolvconf.go b/drivers/shared/resolvconf/resolvconf.go new file mode 100644 index 00000000000..cf098886cfc --- /dev/null +++ b/drivers/shared/resolvconf/resolvconf.go @@ -0,0 +1,45 @@ +package resolvconf + +import ( + "bytes" + "io/ioutil" + "strings" +) + +type ResolvConf struct { + servers []string + searches []string + options []string +} + +func New(servers []string, searches []string, options []string) (*ResolvConf, error) { + return &ResolvConf{ + servers: servers, + searches: searches, + options: options, + } + return nil, nil +} + +func (rc *ResolvConf) Content() []byte { + content := bytes.NewBuffer(nil) + if len(rc.searches) > 0 { + if searchString := strings.Join(rc.searches, " "); strings.Trim(searchString, " ") != "." { + content.WriteString("search " + searchString + "\n") + } + } + for _, dns := range rc.servers { + content.WriteString("nameserver " + dns + "\n") + } + if len(rc.options) > 0 { + if optsString := strings.Join(rc.options, " "); strings.Trim(optsString, " ") != "" { + content.WriteString("options " + optsString + "\n") + } + } + + return content.Bytes() +} + +func (rc *ResolvConf) WriteToPath(path string) error { + return ioutil.WriteFile(path, rc.Content(), 0644) +} diff --git a/drivers/shared/resolvconf/resolvconf_test.go b/drivers/shared/resolvconf/resolvconf_test.go new file mode 100644 index 00000000000..24f05fea197 --- /dev/null +++ b/drivers/shared/resolvconf/resolvconf_test.go @@ -0,0 +1,52 @@ +package resolvconf + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestResolvConf_Content(t *testing.T) { + cases := []struct { + name string + in *ResolvConf + out []byte + }{ + { + name: "empty", + in: &ResolvConf{}, + out: nil, + }, + { + name: "severs", + in: &ResolvConf{servers: []string{"8.8.8.8", "8.8.4.4"}}, + out: []byte("nameserver 8.8.8.8\nnameserver 8.8.4.4\n"), + }, + { + name: "ipv6 servers", + in: &ResolvConf{servers: []string{"2606:4700:4700::1111", "2606:4700:4700::1001"}}, + out: []byte("nameserver 2606:4700:4700::1111\nnameserver 2606:4700:4700::1001\n"), + }, + { + name: "search servers", + in: &ResolvConf{servers: []string{"1.1.1.1", "1.0.0.1"}, searches: []string{"infra.nomad", "local.test"}}, + out: []byte("search infra.nomad local.test\nnameserver 1.1.1.1\nnameserver 1.0.0.1\n"), + }, + { + name: "full example", + in: &ResolvConf{ + servers: []string{"1.1.1.1", "1.0.0.1"}, + searches: []string{"infra.nomad", "local.test"}, + options: []string{"ndots:2", "edns0"}, + }, + out: []byte("search infra.nomad local.test\nnameserver 1.1.1.1\nnameserver 1.0.0.1\noptions ndots:2 edns0\n"), + }, + } + + for _, c := range cases { + t.Run(c.name, func(childT *testing.T) { + childT.Parallel() + require.Equal(childT, c.out, c.in.Content()) + }) + } +} From 49f7d5553b265427686bcdbdd4fd16800e5135cc Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Thu, 9 Apr 2020 17:50:00 -0400 Subject: [PATCH 03/18] fixup: lint --- drivers/docker/driver.go | 2 +- drivers/shared/resolvconf/mount.go | 8 ++++---- drivers/shared/resolvconf/resolvconf.go | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/docker/driver.go b/drivers/docker/driver.go index b0bc5221dfc..bd9ed0bfecb 100644 --- a/drivers/docker/driver.go +++ b/drivers/docker/driver.go @@ -857,7 +857,7 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T hostConfig.ShmSize = driverConfig.ShmSize } - // setup Nomad DNS options, these are overriden by docker driver specific options + // setup Nomad DNS options, these are overridden by docker driver specific options if task.DNS != nil { hostConfig.DNS = task.DNS.Servers hostConfig.DNSSearch = task.DNS.Searches diff --git a/drivers/shared/resolvconf/mount.go b/drivers/shared/resolvconf/mount.go index 0147e94ee9e..1120f962b1c 100644 --- a/drivers/shared/resolvconf/mount.go +++ b/drivers/shared/resolvconf/mount.go @@ -12,9 +12,9 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo var nSearches, nServers, nOptions int path := filepath.Join(taskDir, "resolv.conf") if conf != nil { - nServers := len(conf.Servers) - nSearches := len(conf.Searches) - nOptions := len(conf.Options) + nServers = len(conf.Servers) + nSearches = len(conf.Searches) + nOptions = len(conf.Options) } // Use system dns if no configuration is given @@ -25,7 +25,7 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo return &drivers.MountConfig{ TaskPath: "/etc/resolv.conf", - HostPath: dest, + HostPath: path, Readonly: true, PropagationMode: "private", }, nil diff --git a/drivers/shared/resolvconf/resolvconf.go b/drivers/shared/resolvconf/resolvconf.go index cf098886cfc..7e6ba23abf3 100644 --- a/drivers/shared/resolvconf/resolvconf.go +++ b/drivers/shared/resolvconf/resolvconf.go @@ -17,8 +17,7 @@ func New(servers []string, searches []string, options []string) (*ResolvConf, er servers: servers, searches: searches, options: options, - } - return nil, nil + }, nil } func (rc *ResolvConf) Content() []byte { From e74a2989990c4612576d3e1e975a65461a9911d7 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Thu, 9 Apr 2020 20:04:37 -0400 Subject: [PATCH 04/18] fixup: allocatedresources nil check --- client/allocrunner/taskrunner/task_runner.go | 14 +- plugins/drivers/proto/driver.pb.go | 166 +++++++++---------- 2 files changed, 91 insertions(+), 89 deletions(-) diff --git a/client/allocrunner/taskrunner/task_runner.go b/client/allocrunner/taskrunner/task_runner.go index af92de49832..ca440aa8d67 100644 --- a/client/allocrunner/taskrunner/task_runner.go +++ b/client/allocrunner/taskrunner/task_runner.go @@ -926,7 +926,6 @@ func (tr *TaskRunner) persistLocalState() error { func (tr *TaskRunner) buildTaskConfig() *drivers.TaskConfig { task := tr.Task() alloc := tr.Alloc() - allocDNS := alloc.AllocatedResources.Shared.Networks[0].DNS invocationid := uuid.Generate()[:8] taskResources := tr.taskResources env := tr.envBuilder.Build() @@ -934,11 +933,14 @@ func (tr *TaskRunner) buildTaskConfig() *drivers.TaskConfig { defer tr.networkIsolationLock.Unlock() var dns *drivers.DNSConfig - if allocDNS != nil { - dns = &drivers.DNSConfig{ - Servers: allocDNS.Servers, - Searches: allocDNS.Searches, - Options: allocDNS.Options, + if alloc.AllocatedResources != nil && len(alloc.AllocatedResources.Shared.Networks) > 0 { + allocDNS := alloc.AllocatedResources.Shared.Networks[0].DNS + if allocDNS != nil { + dns = &drivers.DNSConfig{ + Servers: allocDNS.Servers, + Searches: allocDNS.Searches, + Options: allocDNS.Options, + } } } diff --git a/plugins/drivers/proto/driver.pb.go b/plugins/drivers/proto/driver.pb.go index d37160a8db7..33d996adbf1 100644 --- a/plugins/drivers/proto/driver.pb.go +++ b/plugins/drivers/proto/driver.pb.go @@ -2496,7 +2496,7 @@ type LinuxResources struct { // CpusetMems constrains the allowed set of memory nodes. Default: "" (not specified) CpusetMems string `protobuf:"bytes,7,opt,name=cpuset_mems,json=cpusetMems,proto3" json:"cpuset_mems,omitempty"` // PercentTicks is a compatibility option for docker and should not be used - PercentTicks float64 `protobuf:"fixed64,8,opt,name=PercentTicks,json=percentTicks,proto3" json:"PercentTicks,omitempty"` + PercentTicks float64 `protobuf:"fixed64,8,opt,name=PercentTicks,proto3" json:"PercentTicks,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -3485,7 +3485,7 @@ func init() { } var fileDescriptor_4a8f45747846a74d = []byte{ - // 3582 bytes of a gzipped FileDescriptorProto + // 3581 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0x4f, 0x6f, 0x1b, 0x49, 0x76, 0x57, 0xb3, 0x49, 0x8a, 0x7c, 0x94, 0xa8, 0x56, 0x59, 0xf6, 0xd0, 0x9c, 0x24, 0xe3, 0x6d, 0x60, 0x03, 0x61, 0x77, 0x86, 0x9e, 0xd1, 0x22, 0xe3, 0xf1, 0xac, 0x67, 0x3d, 0x1c, 0x8a, 0x96, @@ -3629,87 +3629,87 @@ var fileDescriptor_4a8f45747846a74d = []byte{ 0xf6, 0x19, 0xad, 0x6d, 0xbf, 0x60, 0x7e, 0xc2, 0x0a, 0x26, 0x11, 0xa1, 0x06, 0xfb, 0x70, 0xd7, 0x5a, 0xc5, 0x20, 0x48, 0x9d, 0x60, 0x12, 0xa5, 0x1a, 0xb8, 0xc4, 0x65, 0xee, 0x32, 0xd5, 0xe0, 0x88, 0xb8, 0x6c, 0x94, 0xb5, 0x13, 0x12, 0x5a, 0xc4, 0xa3, 0x03, 0xc7, 0x7a, 0xc9, 0x3c, 0xa1, - 0xb2, 0xad, 0xe0, 0xb5, 0x20, 0x45, 0xd3, 0x7f, 0x06, 0x25, 0xee, 0x39, 0xd9, 0xe4, 0xb9, 0xd7, - 0xe1, 0x4e, 0x49, 0x88, 0xb7, 0xc2, 0x08, 0xdc, 0x25, 0xbd, 0x0f, 0xd5, 0x91, 0x1f, 0x49, 0x97, - 0x26, 0x2c, 0xaf, 0xc2, 0x08, 0xbc, 0xb2, 0x09, 0x95, 0x90, 0x98, 0xb6, 0xef, 0x8d, 0xe3, 0x53, - 0x79, 0x52, 0xd6, 0x5f, 0x41, 0x59, 0x6c, 0xbf, 0xd7, 0xc0, 0xff, 0x08, 0x90, 0x25, 0x7c, 0x61, - 0xc0, 0x4e, 0xf9, 0x51, 0x24, 0x83, 0x33, 0xfe, 0x14, 0x24, 0x6a, 0x4e, 0xa6, 0x15, 0xfa, 0x7f, - 0x29, 0x22, 0x4c, 0x13, 0x97, 0xf4, 0x2c, 0x9e, 0x63, 0x96, 0xc6, 0x8e, 0x34, 0xe2, 0x36, 0x20, - 0x2e, 0xb2, 0x83, 0xb0, 0x8c, 0xc6, 0x0a, 0xcb, 0xbe, 0x71, 0x48, 0x80, 0xf8, 0x6e, 0x90, 0xc8, - 0x53, 0xd3, 0xa2, 0x77, 0x83, 0x44, 0xdc, 0x0d, 0x12, 0x76, 0x76, 0x93, 0x71, 0xa2, 0x80, 0x2b, - 0xf2, 0x30, 0xb1, 0x66, 0x27, 0x17, 0xb0, 0x44, 0xff, 0x1f, 0x25, 0xd9, 0x2b, 0xe2, 0x8b, 0x52, - 0xf4, 0x0d, 0x54, 0xd8, 0xb2, 0x33, 0x5c, 0x33, 0x90, 0xcf, 0x7e, 0x9d, 0xe5, 0xee, 0x60, 0x5b, - 0x6c, 0x95, 0x1d, 0x99, 0x81, 0x88, 0xf2, 0x56, 0x03, 0x51, 0x62, 0x7b, 0x8e, 0x69, 0x4f, 0xf7, - 0x1c, 0xf6, 0x8f, 0xbe, 0x0f, 0x75, 0x73, 0x42, 0x7d, 0xc3, 0xb4, 0xcf, 0x49, 0x48, 0x9d, 0x88, - 0x48, 0xdd, 0xaf, 0x33, 0x6a, 0x3b, 0x26, 0x36, 0x3f, 0x87, 0xb5, 0x34, 0xe6, 0xdb, 0xbc, 0x6f, - 0x29, 0xed, 0x7d, 0xff, 0x04, 0x60, 0x7a, 0xe9, 0xc0, 0x6c, 0x84, 0x5c, 0x38, 0xd4, 0xb0, 0xe2, - 0x73, 0x53, 0x09, 0x57, 0x18, 0xa1, 0xc3, 0x4e, 0x08, 0xd9, 0x1b, 0xd1, 0x52, 0x7c, 0x23, 0xca, - 0x56, 0x2d, 0x5b, 0x68, 0x2f, 0x9d, 0xf1, 0x38, 0xb9, 0x08, 0xa9, 0xfa, 0xbe, 0xfb, 0x98, 0x13, - 0xf4, 0xdf, 0x14, 0x84, 0xad, 0x88, 0xbb, 0xed, 0x5c, 0x21, 0xfd, 0xbb, 0x52, 0xf5, 0x7d, 0x80, - 0x88, 0x9a, 0x21, 0x0b, 0x25, 0xcc, 0xf8, 0x2a, 0xa6, 0x39, 0x73, 0xa5, 0x3a, 0x88, 0x9f, 0xe8, - 0x71, 0x55, 0xb6, 0x6e, 0x53, 0xf4, 0x05, 0xac, 0x59, 0xbe, 0x1b, 0x8c, 0x89, 0xec, 0x5c, 0x7a, - 0x6b, 0xe7, 0x5a, 0xd2, 0xbe, 0x4d, 0x53, 0x17, 0x40, 0xe5, 0xeb, 0x5e, 0x00, 0xfd, 0x9b, 0x22, - 0xae, 0xe8, 0xd3, 0x2f, 0x04, 0x68, 0x38, 0xe7, 0x19, 0x7a, 0x6f, 0xc9, 0xe7, 0x86, 0xef, 0x7a, - 0x83, 0x6e, 0x7e, 0x91, 0xe7, 0xd1, 0xf7, 0xcd, 0xc1, 0xdd, 0xbf, 0xab, 0x50, 0x4d, 0x6e, 0xe7, - 0x67, 0x74, 0xff, 0x19, 0x54, 0x93, 0xfc, 0x08, 0xb9, 0x41, 0x7c, 0xa7, 0x7a, 0x92, 0xc6, 0xe8, - 0x39, 0x20, 0x73, 0x38, 0x4c, 0x82, 0x36, 0x63, 0x12, 0x99, 0xc3, 0xf8, 0x6d, 0xe4, 0xb3, 0x05, - 0xe4, 0x10, 0xfb, 0xad, 0x53, 0xd6, 0x1f, 0x6b, 0xe6, 0x70, 0x98, 0xa1, 0xa0, 0x3f, 0x85, 0x9b, - 0xd9, 0x31, 0x8c, 0xb3, 0x4b, 0x23, 0x70, 0x6c, 0x79, 0x74, 0xdc, 0x5f, 0xf4, 0x81, 0xa2, 0x95, - 0x81, 0xff, 0xea, 0xf2, 0xc4, 0xb1, 0x85, 0xcc, 0x51, 0x38, 0x53, 0xd1, 0xfc, 0x73, 0x78, 0xef, - 0x0d, 0xcd, 0xe7, 0xe8, 0xa0, 0x97, 0x7d, 0x78, 0x5f, 0x5e, 0x08, 0x29, 0xed, 0xfd, 0x5a, 0x11, - 0xef, 0x28, 0x59, 0x99, 0xb4, 0xd3, 0x71, 0xeb, 0xdd, 0x9c, 0xe3, 0x74, 0x4e, 0x4e, 0x05, 0x3c, - 0x0f, 0x55, 0xbf, 0xbe, 0x12, 0xaa, 0xe6, 0x0d, 0x62, 0x44, 0xc4, 0x27, 0x80, 0x24, 0x82, 0xfe, - 0x2f, 0x2a, 0x54, 0x62, 0x74, 0x7e, 0xf0, 0xbb, 0x8c, 0x28, 0x71, 0x8d, 0xe4, 0xea, 0x47, 0xc1, - 0x20, 0x48, 0xfc, 0x9a, 0xe3, 0x7d, 0xa8, 0xb2, 0xf3, 0xa5, 0xa8, 0x2e, 0xf0, 0xea, 0x0a, 0x23, - 0xf0, 0xca, 0x0f, 0xa0, 0x46, 0x7d, 0x6a, 0x8e, 0x0d, 0xca, 0x7d, 0xb9, 0x2a, 0x7a, 0x73, 0x12, - 0xf7, 0xe4, 0xe8, 0x87, 0xb0, 0x49, 0x47, 0xa1, 0x4f, 0xe9, 0x98, 0xc5, 0x77, 0x3c, 0xa2, 0x11, - 0x01, 0x48, 0x11, 0x6b, 0x49, 0x85, 0x88, 0x74, 0x22, 0xb6, 0x7b, 0x4f, 0x1b, 0x33, 0xd3, 0xe5, - 0x9b, 0x48, 0x11, 0xaf, 0x27, 0x54, 0x66, 0xda, 0xcc, 0x79, 0xca, 0x68, 0x81, 0xef, 0x15, 0x0a, - 0x8e, 0x8b, 0xc8, 0x80, 0x0d, 0x97, 0x98, 0xd1, 0x24, 0x24, 0xb6, 0xf1, 0xdc, 0x21, 0x63, 0x5b, - 0x9c, 0xd7, 0xeb, 0xb9, 0xc3, 0xef, 0x58, 0x2c, 0xad, 0x47, 0xbc, 0x37, 0xae, 0xc7, 0x70, 0xa2, - 0xcc, 0x22, 0x07, 0xf1, 0x87, 0x36, 0xa0, 0xd6, 0x7f, 0xda, 0x1f, 0x74, 0x8f, 0x8c, 0xa3, 0xe3, - 0xdd, 0xae, 0xcc, 0xad, 0xe8, 0x77, 0xb1, 0x28, 0x2a, 0xac, 0x7e, 0x70, 0x3c, 0x68, 0x1f, 0x1a, - 0x83, 0x83, 0xce, 0xe3, 0xbe, 0x56, 0x40, 0x37, 0x61, 0x73, 0xb0, 0x8f, 0x8f, 0x07, 0x83, 0xc3, - 0xee, 0xae, 0x71, 0xd2, 0xc5, 0x07, 0xc7, 0xbb, 0x7d, 0x4d, 0x45, 0x08, 0xea, 0x53, 0xf2, 0xe0, - 0xe0, 0xa8, 0xab, 0x15, 0x51, 0x0d, 0x56, 0x4f, 0xba, 0xb8, 0xd3, 0xed, 0x0d, 0xb4, 0x92, 0xfe, - 0x2b, 0x15, 0x6a, 0x29, 0x2d, 0x32, 0x43, 0x0e, 0x23, 0x11, 0xe7, 0x17, 0x31, 0xfb, 0xe5, 0x6f, - 0x41, 0xa6, 0x35, 0x12, 0xda, 0x29, 0x62, 0x51, 0xe0, 0xb1, 0xbd, 0x79, 0x91, 0x5a, 0xe7, 0x45, - 0x5c, 0x71, 0xcd, 0x0b, 0x01, 0xf2, 0x3d, 0x58, 0x7b, 0x49, 0x42, 0x8f, 0x8c, 0x65, 0xbd, 0xd0, - 0x48, 0x4d, 0xd0, 0x44, 0x93, 0x6d, 0xd0, 0x64, 0x93, 0x29, 0x8c, 0x50, 0x47, 0x5d, 0xd0, 0x8f, - 0x62, 0xb0, 0x2d, 0x28, 0x89, 0xea, 0x55, 0x31, 0x3e, 0x2f, 0x30, 0x37, 0x15, 0xbd, 0x36, 0x03, - 0x1e, 0xdf, 0x15, 0x31, 0xff, 0x47, 0x67, 0xb3, 0xfa, 0x29, 0x73, 0xfd, 0xdc, 0x5f, 0xdc, 0x9c, - 0xdf, 0xa4, 0xa2, 0x51, 0xa2, 0xa2, 0x55, 0x50, 0x71, 0x9c, 0x90, 0xd0, 0x69, 0x77, 0xf6, 0x99, - 0x5a, 0xd6, 0xa1, 0x7a, 0xd4, 0xfe, 0xa9, 0x71, 0xda, 0xe7, 0x57, 0xa1, 0x48, 0x83, 0xb5, 0xc7, - 0x5d, 0xdc, 0xeb, 0x1e, 0x4a, 0x8a, 0x8a, 0xb6, 0x40, 0x93, 0x94, 0x69, 0xbb, 0x22, 0x43, 0x10, - 0xbf, 0x25, 0x54, 0x81, 0x62, 0xff, 0x49, 0xfb, 0x44, 0x2b, 0xeb, 0xff, 0x5d, 0x80, 0x0d, 0xe1, - 0x16, 0x92, 0xa7, 0xd3, 0x37, 0x3f, 0x1d, 0xa5, 0x2f, 0x3f, 0x0a, 0xd9, 0xcb, 0x8f, 0x38, 0x08, - 0xe5, 0x5e, 0x5d, 0x9d, 0x06, 0xa1, 0xfc, 0xd2, 0x24, 0xb3, 0xe3, 0x17, 0x17, 0xd9, 0xf1, 0x1b, - 0xb0, 0xea, 0x92, 0x28, 0xd1, 0x5b, 0x15, 0xc7, 0x45, 0xe4, 0x40, 0xcd, 0xf4, 0x3c, 0x9f, 0x9a, - 0xe2, 0x46, 0xb1, 0xbc, 0x90, 0x33, 0xbc, 0x32, 0xe3, 0x56, 0x7b, 0x8a, 0x24, 0x36, 0xe6, 0x34, - 0x76, 0xf3, 0x27, 0xa0, 0x5d, 0x6d, 0xb0, 0x88, 0x3b, 0xfc, 0xc1, 0x27, 0x53, 0x6f, 0x48, 0xd8, - 0xba, 0x38, 0xed, 0x3d, 0xee, 0x1d, 0x3f, 0xe9, 0x69, 0x2b, 0xac, 0x80, 0x4f, 0x7b, 0xbd, 0x83, - 0xde, 0x9e, 0xa6, 0x20, 0x80, 0x72, 0xf7, 0xa7, 0x07, 0x83, 0xee, 0xae, 0x56, 0xd8, 0xf9, 0xf5, - 0x26, 0x94, 0x05, 0x93, 0xe8, 0x5b, 0x19, 0x09, 0xa4, 0xd3, 0xf2, 0xd0, 0x4f, 0x16, 0x8e, 0xa8, - 0x33, 0xa9, 0x7e, 0xcd, 0x87, 0x4b, 0xf7, 0x97, 0x4f, 0x24, 0x2b, 0xe8, 0xaf, 0x15, 0x58, 0xcb, - 0x3c, 0x8f, 0xe4, 0xbd, 0x51, 0x9d, 0x93, 0x05, 0xd8, 0xfc, 0xf1, 0x52, 0x7d, 0x13, 0x5e, 0x7e, - 0xa9, 0x40, 0x2d, 0x95, 0xff, 0x86, 0xee, 0x2f, 0x93, 0x33, 0x27, 0x38, 0xf9, 0x7c, 0xf9, 0x74, - 0x3b, 0x7d, 0xe5, 0x63, 0x05, 0xfd, 0x95, 0x02, 0xb5, 0x54, 0x26, 0x58, 0x6e, 0x56, 0x66, 0xf3, - 0xd6, 0x72, 0xb3, 0x32, 0x2f, 0xf1, 0x6c, 0x05, 0xfd, 0x85, 0x02, 0xd5, 0x24, 0xab, 0x0b, 0xdd, - 0x5b, 0x3c, 0x0f, 0x4c, 0x30, 0xf1, 0xd9, 0xb2, 0x09, 0x64, 0xfa, 0x0a, 0xfa, 0x33, 0xa8, 0xc4, - 0x29, 0x50, 0x28, 0xaf, 0xf7, 0xba, 0x92, 0x5f, 0xd5, 0xbc, 0xb7, 0x70, 0xbf, 0xf4, 0xf0, 0x71, - 0x5e, 0x52, 0xee, 0xe1, 0xaf, 0x64, 0x50, 0x35, 0xef, 0x2d, 0xdc, 0x2f, 0x19, 0x9e, 0x59, 0x42, - 0x2a, 0x7d, 0x29, 0xb7, 0x25, 0xcc, 0xe6, 0x4d, 0xe5, 0xb6, 0x84, 0x79, 0xd9, 0x52, 0x82, 0x91, - 0x54, 0x02, 0x54, 0x6e, 0x46, 0x66, 0x93, 0xac, 0x72, 0x33, 0x32, 0x27, 0xdf, 0x4a, 0x5f, 0x41, - 0xbf, 0x50, 0xd2, 0xe7, 0x82, 0x7b, 0x0b, 0xe7, 0xf9, 0x2c, 0x68, 0x92, 0x33, 0x99, 0x46, 0x7c, - 0x81, 0xfe, 0x42, 0xde, 0x62, 0x88, 0x34, 0x21, 0xb4, 0x08, 0x58, 0x26, 0xb3, 0xa8, 0xf9, 0xe9, - 0x72, 0xce, 0x86, 0x33, 0xf1, 0x97, 0x0a, 0xc0, 0x34, 0xa1, 0x28, 0x37, 0x13, 0x33, 0x99, 0x4c, - 0xcd, 0xfb, 0x4b, 0xf4, 0x4c, 0x2f, 0x90, 0x38, 0xe1, 0x21, 0xf7, 0x02, 0xb9, 0x92, 0xf0, 0x94, - 0x7b, 0x81, 0x5c, 0x4d, 0x56, 0xd2, 0x57, 0xd0, 0x3f, 0x2a, 0xb0, 0x39, 0x93, 0x70, 0x81, 0x1e, - 0x5e, 0x33, 0xe7, 0xa6, 0xf9, 0xe5, 0xf2, 0x00, 0x31, 0x6b, 0xdb, 0xca, 0xc7, 0x0a, 0xfa, 0x1b, - 0x05, 0xd6, 0x33, 0x8f, 0xd4, 0x28, 0xb7, 0x97, 0x9a, 0x93, 0xba, 0xd1, 0x7c, 0xb0, 0x5c, 0xe7, - 0x44, 0x5a, 0x7f, 0xa7, 0x40, 0x3d, 0x9b, 0xaf, 0x80, 0x1e, 0x2c, 0xb6, 0x2d, 0x5c, 0x61, 0xe8, - 0x8b, 0x25, 0x7b, 0xc7, 0x1c, 0x7d, 0xb5, 0xfa, 0x47, 0x25, 0x11, 0xbd, 0x95, 0xf9, 0xe7, 0x47, - 0xbf, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x1c, 0x14, 0x37, 0x01, 0x73, 0x31, 0x00, 0x00, + 0xb2, 0xad, 0xe0, 0x0c, 0x4d, 0xff, 0x19, 0x94, 0xb8, 0xe7, 0x64, 0x93, 0xe7, 0x5e, 0x87, 0x3b, + 0x25, 0x21, 0xde, 0x0a, 0x23, 0x70, 0x97, 0xf4, 0x3e, 0x54, 0x47, 0x7e, 0x24, 0x5d, 0x9a, 0xb0, + 0xbc, 0x0a, 0x23, 0xf0, 0xca, 0x26, 0x54, 0x42, 0x62, 0xda, 0xbe, 0x37, 0x8e, 0x4f, 0xe5, 0x49, + 0x59, 0x7f, 0x05, 0x65, 0xb1, 0xfd, 0x5e, 0x03, 0xff, 0x23, 0x40, 0x96, 0xf0, 0x85, 0x01, 0x3b, + 0xe5, 0x47, 0x91, 0x0c, 0xce, 0xf8, 0x53, 0x90, 0xa8, 0x39, 0x99, 0x56, 0xe8, 0xff, 0xa5, 0x88, + 0x30, 0x4d, 0x5c, 0xd2, 0xb3, 0x78, 0x8e, 0x59, 0x1a, 0x3b, 0xd2, 0x88, 0xdb, 0x80, 0xb8, 0xc8, + 0x0e, 0xc2, 0x32, 0x1a, 0x2b, 0x2c, 0xfb, 0xc6, 0x21, 0x01, 0xe2, 0xbb, 0x41, 0x22, 0x4f, 0x4d, + 0x8b, 0xde, 0x0d, 0x12, 0x71, 0x37, 0x48, 0xd8, 0xd9, 0x4d, 0xc6, 0x89, 0x02, 0xae, 0xc8, 0xc3, + 0xc4, 0x9a, 0x9d, 0x5c, 0xc0, 0x12, 0xfd, 0x7f, 0x94, 0x64, 0xaf, 0x88, 0x2f, 0x4a, 0xd1, 0x37, + 0x50, 0x61, 0xcb, 0xce, 0x70, 0xcd, 0x40, 0x3e, 0xfb, 0x75, 0x96, 0xbb, 0x83, 0x6d, 0xb1, 0x55, + 0x76, 0x64, 0x06, 0x22, 0xca, 0x5b, 0x0d, 0x44, 0x89, 0xed, 0x39, 0xa6, 0x3d, 0xdd, 0x73, 0xd8, + 0x3f, 0xfa, 0x3e, 0xd4, 0xcd, 0x09, 0xf5, 0x0d, 0xd3, 0x3e, 0x27, 0x21, 0x75, 0x22, 0x22, 0x75, + 0xbf, 0xce, 0xa8, 0xed, 0x98, 0xd8, 0xfc, 0x1c, 0xd6, 0xd2, 0x98, 0x6f, 0xf3, 0xbe, 0xa5, 0xb4, + 0xf7, 0xfd, 0x13, 0x80, 0xe9, 0xa5, 0x03, 0xb3, 0x11, 0x72, 0xe1, 0x50, 0xc3, 0x8a, 0xcf, 0x4d, + 0x25, 0x5c, 0x61, 0x84, 0x0e, 0x3b, 0x21, 0x64, 0x6f, 0x44, 0x4b, 0xf1, 0x8d, 0x28, 0x5b, 0xb5, + 0x6c, 0xa1, 0xbd, 0x74, 0xc6, 0xe3, 0xe4, 0x22, 0xa4, 0xea, 0xfb, 0xee, 0x63, 0x4e, 0xd0, 0x7f, + 0x53, 0x10, 0xb6, 0x22, 0xee, 0xb6, 0x73, 0x85, 0xf4, 0xef, 0x4a, 0xd5, 0xf7, 0x01, 0x22, 0x6a, + 0x86, 0x2c, 0x94, 0x30, 0xe3, 0xab, 0x98, 0xe6, 0xcc, 0x95, 0xea, 0x20, 0x7e, 0xa2, 0xc7, 0x55, + 0xd9, 0xba, 0x4d, 0xd1, 0x17, 0xb0, 0x66, 0xf9, 0x6e, 0x30, 0x26, 0xb2, 0x73, 0xe9, 0xad, 0x9d, + 0x6b, 0x49, 0xfb, 0x36, 0x4d, 0x5d, 0x00, 0x95, 0xaf, 0x7b, 0x01, 0xf4, 0x6f, 0x8a, 0xb8, 0xa2, + 0x4f, 0xbf, 0x10, 0xa0, 0xe1, 0x9c, 0x67, 0xe8, 0xbd, 0x25, 0x9f, 0x1b, 0xbe, 0xeb, 0x0d, 0xba, + 0xf9, 0x45, 0x9e, 0x47, 0xdf, 0x37, 0x07, 0x77, 0xff, 0xae, 0x42, 0x35, 0xb9, 0x9d, 0x9f, 0xd1, + 0xfd, 0x67, 0x50, 0x4d, 0xf2, 0x23, 0xe4, 0x06, 0xf1, 0x9d, 0xea, 0x49, 0x1a, 0xa3, 0xe7, 0x80, + 0xcc, 0xe1, 0x30, 0x09, 0xda, 0x8c, 0x49, 0x64, 0x0e, 0xe3, 0xb7, 0x91, 0xcf, 0x16, 0x90, 0x43, + 0xec, 0xb7, 0x4e, 0x59, 0x7f, 0xac, 0x99, 0xc3, 0x61, 0x86, 0x82, 0xfe, 0x14, 0x6e, 0x66, 0xc7, + 0x30, 0xce, 0x2e, 0x8d, 0xc0, 0xb1, 0xe5, 0xd1, 0x71, 0x7f, 0xd1, 0x07, 0x8a, 0x56, 0x06, 0xfe, + 0xab, 0xcb, 0x13, 0xc7, 0x16, 0x32, 0x47, 0xe1, 0x4c, 0x45, 0xf3, 0xcf, 0xe1, 0xbd, 0x37, 0x34, + 0x9f, 0xa3, 0x83, 0x5e, 0xf6, 0xe1, 0x7d, 0x79, 0x21, 0xa4, 0xb4, 0xf7, 0x6b, 0x45, 0xbc, 0xa3, + 0x64, 0x65, 0xd2, 0x4e, 0xc7, 0xad, 0x77, 0x73, 0x8e, 0xd3, 0x39, 0x39, 0x15, 0xf0, 0x3c, 0x54, + 0xfd, 0xfa, 0x4a, 0xa8, 0x9a, 0x37, 0x88, 0x11, 0x11, 0x9f, 0x00, 0x92, 0x08, 0xfa, 0xbf, 0xa8, + 0x50, 0x89, 0xd1, 0xf9, 0xc1, 0xef, 0x32, 0xa2, 0xc4, 0x35, 0x92, 0xab, 0x1f, 0x05, 0x83, 0x20, + 0xf1, 0x6b, 0x8e, 0xf7, 0xa1, 0xca, 0xce, 0x97, 0xa2, 0xba, 0xc0, 0xab, 0x2b, 0x8c, 0xc0, 0x2b, + 0x3f, 0x80, 0x1a, 0xf5, 0xa9, 0x39, 0x36, 0x28, 0xf7, 0xe5, 0xaa, 0xe8, 0xcd, 0x49, 0xdc, 0x93, + 0xa3, 0x1f, 0xc2, 0x26, 0x1d, 0x85, 0x3e, 0xa5, 0x63, 0x16, 0xdf, 0xf1, 0x88, 0x46, 0x04, 0x20, + 0x45, 0xac, 0x25, 0x15, 0x22, 0xd2, 0x89, 0xd8, 0xee, 0x3d, 0x6d, 0xcc, 0x4c, 0x97, 0x6f, 0x22, + 0x45, 0xbc, 0x9e, 0x50, 0x99, 0x69, 0x33, 0xe7, 0x19, 0x88, 0x68, 0x81, 0xef, 0x15, 0x0a, 0x8e, + 0x8b, 0xc8, 0x80, 0x0d, 0x97, 0x98, 0xd1, 0x24, 0x24, 0xb6, 0xf1, 0xdc, 0x21, 0x63, 0x5b, 0x9c, + 0xd7, 0xeb, 0xb9, 0xc3, 0xef, 0x58, 0x2c, 0xad, 0x47, 0xbc, 0x37, 0xae, 0xc7, 0x70, 0xa2, 0xcc, + 0x22, 0x07, 0xf1, 0x87, 0x36, 0xa0, 0xd6, 0x7f, 0xda, 0x1f, 0x74, 0x8f, 0x8c, 0xa3, 0xe3, 0xdd, + 0xae, 0xcc, 0xad, 0xe8, 0x77, 0xb1, 0x28, 0x2a, 0xac, 0x7e, 0x70, 0x3c, 0x68, 0x1f, 0x1a, 0x83, + 0x83, 0xce, 0xe3, 0xbe, 0x56, 0x40, 0x37, 0x61, 0x73, 0xb0, 0x8f, 0x8f, 0x07, 0x83, 0xc3, 0xee, + 0xae, 0x71, 0xd2, 0xc5, 0x07, 0xc7, 0xbb, 0x7d, 0x4d, 0x45, 0x08, 0xea, 0x53, 0xf2, 0xe0, 0xe0, + 0xa8, 0xab, 0x15, 0x51, 0x0d, 0x56, 0x4f, 0xba, 0xb8, 0xd3, 0xed, 0x0d, 0xb4, 0x92, 0xfe, 0x2b, + 0x15, 0x6a, 0x29, 0x2d, 0x32, 0x43, 0x0e, 0x23, 0x11, 0xe7, 0x17, 0x31, 0xfb, 0xe5, 0x6f, 0x41, + 0xa6, 0x35, 0x12, 0xda, 0x29, 0x62, 0x51, 0xe0, 0xb1, 0xbd, 0x79, 0x91, 0x5a, 0xe7, 0x45, 0x5c, + 0x71, 0xcd, 0x0b, 0x01, 0xf2, 0x3d, 0x58, 0x7b, 0x49, 0x42, 0x8f, 0x8c, 0x65, 0xbd, 0xd0, 0x48, + 0x4d, 0xd0, 0x44, 0x93, 0x6d, 0xd0, 0x64, 0x93, 0x29, 0x8c, 0x50, 0x47, 0x5d, 0xd0, 0x8f, 0x62, + 0xb0, 0x2d, 0x28, 0x89, 0xea, 0x55, 0x31, 0x3e, 0x2f, 0x30, 0x37, 0x15, 0xbd, 0x36, 0x03, 0x1e, + 0xdf, 0x15, 0x31, 0xff, 0x47, 0x67, 0xb3, 0xfa, 0x29, 0x73, 0xfd, 0xdc, 0x5f, 0xdc, 0x9c, 0xdf, + 0xa4, 0xa2, 0x51, 0xa2, 0xa2, 0x55, 0x50, 0x71, 0x9c, 0x90, 0xd0, 0x69, 0x77, 0xf6, 0x99, 0x5a, + 0xd6, 0xa1, 0x7a, 0xd4, 0xfe, 0xa9, 0x71, 0xda, 0xe7, 0x57, 0xa1, 0x48, 0x83, 0xb5, 0xc7, 0x5d, + 0xdc, 0xeb, 0x1e, 0x4a, 0x8a, 0x8a, 0xb6, 0x40, 0x93, 0x94, 0x69, 0xbb, 0x22, 0x43, 0x10, 0xbf, + 0x25, 0x54, 0x81, 0x62, 0xff, 0x49, 0xfb, 0x44, 0x2b, 0xeb, 0xff, 0x5d, 0x80, 0x0d, 0xe1, 0x16, + 0x92, 0xa7, 0xd3, 0x37, 0x3f, 0x1d, 0xa5, 0x2f, 0x3f, 0x0a, 0xd9, 0xcb, 0x8f, 0x38, 0x08, 0xe5, + 0x5e, 0x5d, 0x9d, 0x06, 0xa1, 0xfc, 0xd2, 0x24, 0xb3, 0xe3, 0x17, 0x17, 0xd9, 0xf1, 0x1b, 0xb0, + 0xea, 0x92, 0x28, 0xd1, 0x5b, 0x15, 0xc7, 0x45, 0xe4, 0x40, 0xcd, 0xf4, 0x3c, 0x9f, 0x9a, 0xe2, + 0x46, 0xb1, 0xbc, 0x90, 0x33, 0xbc, 0x32, 0xe3, 0x56, 0x7b, 0x8a, 0x24, 0x36, 0xe6, 0x34, 0x76, + 0xf3, 0x27, 0xa0, 0x5d, 0x6d, 0xb0, 0x88, 0x3b, 0xfc, 0xc1, 0x27, 0x53, 0x6f, 0x48, 0xd8, 0xba, + 0x38, 0xed, 0x3d, 0xee, 0x1d, 0x3f, 0xe9, 0x69, 0x2b, 0xac, 0x80, 0x4f, 0x7b, 0xbd, 0x83, 0xde, + 0x9e, 0xa6, 0x20, 0x80, 0x72, 0xf7, 0xa7, 0x07, 0x83, 0xee, 0xae, 0x56, 0xd8, 0xf9, 0xf5, 0x26, + 0x94, 0x05, 0x93, 0xe8, 0x5b, 0x19, 0x09, 0xa4, 0xd3, 0xf2, 0xd0, 0x4f, 0x16, 0x8e, 0xa8, 0x33, + 0xa9, 0x7e, 0xcd, 0x87, 0x4b, 0xf7, 0x97, 0x4f, 0x24, 0x2b, 0xe8, 0xaf, 0x15, 0x58, 0xcb, 0x3c, + 0x8f, 0xe4, 0xbd, 0x51, 0x9d, 0x93, 0x05, 0xd8, 0xfc, 0xf1, 0x52, 0x7d, 0x13, 0x5e, 0x7e, 0xa9, + 0x40, 0x2d, 0x95, 0xff, 0x86, 0xee, 0x2f, 0x93, 0x33, 0x27, 0x38, 0xf9, 0x7c, 0xf9, 0x74, 0x3b, + 0x7d, 0xe5, 0x63, 0x05, 0xfd, 0x95, 0x02, 0xb5, 0x54, 0x26, 0x58, 0x6e, 0x56, 0x66, 0xf3, 0xd6, + 0x72, 0xb3, 0x32, 0x2f, 0xf1, 0x6c, 0x05, 0xfd, 0x85, 0x02, 0xd5, 0x24, 0xab, 0x0b, 0xdd, 0x5b, + 0x3c, 0x0f, 0x4c, 0x30, 0xf1, 0xd9, 0xb2, 0x09, 0x64, 0xfa, 0x0a, 0xfa, 0x33, 0xa8, 0xc4, 0x29, + 0x50, 0x28, 0xaf, 0xf7, 0xba, 0x92, 0x5f, 0xd5, 0xbc, 0xb7, 0x70, 0xbf, 0xf4, 0xf0, 0x71, 0x5e, + 0x52, 0xee, 0xe1, 0xaf, 0x64, 0x50, 0x35, 0xef, 0x2d, 0xdc, 0x2f, 0x19, 0x9e, 0x59, 0x42, 0x2a, + 0x7d, 0x29, 0xb7, 0x25, 0xcc, 0xe6, 0x4d, 0xe5, 0xb6, 0x84, 0x79, 0xd9, 0x52, 0x82, 0x91, 0x54, + 0x02, 0x54, 0x6e, 0x46, 0x66, 0x93, 0xac, 0x72, 0x33, 0x32, 0x27, 0xdf, 0x4a, 0x5f, 0x41, 0xbf, + 0x50, 0xd2, 0xe7, 0x82, 0x7b, 0x0b, 0xe7, 0xf9, 0x2c, 0x68, 0x92, 0x33, 0x99, 0x46, 0x7c, 0x81, + 0xfe, 0x42, 0xde, 0x62, 0x88, 0x34, 0x21, 0xb4, 0x08, 0x58, 0x26, 0xb3, 0xa8, 0xf9, 0xe9, 0x72, + 0xce, 0x86, 0x33, 0xf1, 0x97, 0x0a, 0xc0, 0x34, 0xa1, 0x28, 0x37, 0x13, 0x33, 0x99, 0x4c, 0xcd, + 0xfb, 0x4b, 0xf4, 0x4c, 0x2f, 0x90, 0x38, 0xe1, 0x21, 0xf7, 0x02, 0xb9, 0x92, 0xf0, 0x94, 0x7b, + 0x81, 0x5c, 0x4d, 0x56, 0xd2, 0x57, 0xd0, 0x3f, 0x2a, 0xb0, 0x39, 0x93, 0x70, 0x81, 0x1e, 0x5e, + 0x33, 0xe7, 0xa6, 0xf9, 0xe5, 0xf2, 0x00, 0x31, 0x6b, 0xdb, 0xca, 0xc7, 0x0a, 0xfa, 0x1b, 0x05, + 0xd6, 0x33, 0x8f, 0xd4, 0x28, 0xb7, 0x97, 0x9a, 0x93, 0xba, 0xd1, 0x7c, 0xb0, 0x5c, 0xe7, 0x44, + 0x5a, 0x7f, 0xa7, 0x40, 0x3d, 0x9b, 0xaf, 0x80, 0x1e, 0x2c, 0xb6, 0x2d, 0x5c, 0x61, 0xe8, 0x8b, + 0x25, 0x7b, 0xc7, 0x1c, 0x7d, 0xb5, 0xfa, 0x47, 0x25, 0x11, 0xbd, 0x95, 0xf9, 0xe7, 0x47, 0xbf, + 0x0d, 0x00, 0x00, 0xff, 0xff, 0x2e, 0xf3, 0xee, 0x23, 0x73, 0x31, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. From 54eaaae660dd78ae5403311499ca78e5a71c9d70 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Fri, 10 Apr 2020 10:44:32 -0400 Subject: [PATCH 05/18] drivers: add driver dns config conformance test --- drivers/exec/driver_unix_test.go | 50 ++++++++++++ drivers/shared/resolvconf/mount.go | 20 ++--- drivers/shared/resolvconf/mount_unix_test.go | 30 +++++++ plugins/drivers/testutils/dns_testing.go | 86 ++++++++++++++++++++ 4 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 drivers/shared/resolvconf/mount_unix_test.go create mode 100644 plugins/drivers/testutils/dns_testing.go diff --git a/drivers/exec/driver_unix_test.go b/drivers/exec/driver_unix_test.go index 342993a8f4f..e6a59af4028 100644 --- a/drivers/exec/driver_unix_test.go +++ b/drivers/exec/driver_unix_test.go @@ -107,3 +107,53 @@ func TestExec_ExecTaskStreaming(t *testing.T) { dtestutil.ExecTaskStreamingConformanceTests(t, harness, task.ID) } + +// Tests that a given DNSConfig properly configures dns +func TestExec_dnsConfig(t *testing.T) { + t.Parallel() + ctestutils.RequireRoot(t) + ctestutils.ExecCompatible(t) + require := require.New(t) + d := NewExecDriver(testlog.HCLogger(t)) + harness := dtestutil.NewDriverHarness(t, d) + defer harness.Kill() + + cases := []struct { + name string + cfg *drivers.DNSConfig + }{ + { + name: "nil DNSConfig", + }, + { + name: "basic", + cfg: &drivers.DNSConfig{ + Servers: []string{"1.1.1.1", "1.0.0.1"}, + }, + }, + } + + for _, c := range cases { + task := &drivers.TaskConfig{ + ID: uuid.Generate(), + Name: "sleep", + DNS: c.cfg, + } + + cleanup := harness.MkAllocDir(task, false) + defer cleanup() + + tc := &TaskConfig{ + Command: "/bin/sleep", + Args: []string{"9000"}, + } + require.NoError(task.EncodeConcreteDriverConfig(&tc)) + + _, _, err := harness.StartTask(task) + require.NoError(err) + defer d.DestroyTask(task.ID, true) + + dtestutil.TestTaskDNSConfig(t, harness, task.ID, c.cfg) + } + +} diff --git a/drivers/shared/resolvconf/mount.go b/drivers/shared/resolvconf/mount.go index 1120f962b1c..a102d2e27ed 100644 --- a/drivers/shared/resolvconf/mount.go +++ b/drivers/shared/resolvconf/mount.go @@ -11,6 +11,12 @@ import ( func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountConfig, error) { var nSearches, nServers, nOptions int path := filepath.Join(taskDir, "resolv.conf") + mount := &drivers.MountConfig{ + TaskPath: "/etc/resolv.conf", + HostPath: path, + Readonly: true, + PropagationMode: "private", + } if conf != nil { nServers = len(conf.Servers) nSearches = len(conf.Searches) @@ -23,12 +29,7 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo return nil, err } - return &drivers.MountConfig{ - TaskPath: "/etc/resolv.conf", - HostPath: path, - Readonly: true, - PropagationMode: "private", - }, nil + return mount, nil } f, err := os.Create(path) @@ -46,12 +47,7 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo return nil, err } - return &drivers.MountConfig{ - TaskPath: "/etc/resolv.conf", - HostPath: path, - Readonly: true, - PropagationMode: "private", - }, nil + return mount, nil } func copySystemDNS(dest string) error { diff --git a/drivers/shared/resolvconf/mount_unix_test.go b/drivers/shared/resolvconf/mount_unix_test.go new file mode 100644 index 00000000000..76154f5909b --- /dev/null +++ b/drivers/shared/resolvconf/mount_unix_test.go @@ -0,0 +1,30 @@ +// +build !windows + +package resolvconf + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_copySystemDNS(t *testing.T) { + require := require.New(t) + data, err := ioutil.ReadFile("/etc/resolv.conf") + require.NoError(err) + + tmp, err := ioutil.TempDir("", "copySystemDNS_Test") + require.NoError(err) + defer os.RemoveAll(tmp) + dest := filepath.Join(tmp, "resolv.conf") + + require.NoError(copySystemDNS(dest)) + require.FileExists(dest) + + tmpResolv, err := ioutil.ReadFile(dest) + require.NoError(err) + require.Equal(data, tmpResolv) +} diff --git a/plugins/drivers/testutils/dns_testing.go b/plugins/drivers/testutils/dns_testing.go new file mode 100644 index 00000000000..51afae2908b --- /dev/null +++ b/plugins/drivers/testutils/dns_testing.go @@ -0,0 +1,86 @@ +package testutils + +import ( + "io/ioutil" + "strings" + "testing" + + "github.com/hashicorp/nomad/plugins/drivers" + "github.com/stretchr/testify/require" +) + +// TestTaskDNSConfig asserts that a task is running with the given DNSConfig +func TestTaskDNSConfig(t *testing.T, driver *DriverHarness, taskID string, dns *drivers.DNSConfig) { + t.Run("dns_config", func(t *testing.T) { + caps, err := driver.Capabilities() + require.NoError(t, err) + + isolated := (caps.FSIsolation != drivers.FSIsolationNone) + if !isolated { + t.Skip("dns config not supported on non isolated drivers") + } + + // write to a file and check it presence in host + r := execTask(t, driver, taskID, `cat /etc/resolv.conf`, + false, "") + require.Zero(t, r.exitCode) + + resolvConf := strings.TrimSpace(r.stdout) + + if dns != nil { + require.ElementsMatch(t, dns.Servers, getNameservers(resolvConf)) + require.ElementsMatch(t, dns.Searches, getSearchDomains(resolvConf)) + require.ElementsMatch(t, dns.Searches, getOptions(resolvConf)) + } else { + system, err := ioutil.ReadFile("/etc/resolv.conf") + require.NoError(t, err) + require.ElementsMatch(t, getNameservers(string(system)), getNameservers(resolvConf)) + require.ElementsMatch(t, getSearchDomains(string(system)), getSearchDomains(resolvConf)) + require.ElementsMatch(t, getOptions(string(system)), getOptions(resolvConf)) + } + }) +} + +// getLines parses input into lines and strips away # comments. +func getLines(input string) []string { + lines := strings.Split(input, "\n") + var output []string + for _, currentLine := range lines { + var commentIndex = strings.Index(currentLine, "#") + if commentIndex == -1 { + output = append(output, currentLine) + } else { + output = append(output, currentLine[:commentIndex]) + } + } + return output +} +func getNameservers(resolvConf string) []string { + nameservers := []string{} + for _, line := range getLines(resolvConf) { + if strings.HasPrefix(line, "nameserver ") { + nameservers = append(nameservers, line[11:]) + } + } + return nameservers +} + +func getSearchDomains(resolvConf string) []string { + domains := []string{} + for _, line := range getLines(resolvConf) { + if strings.HasPrefix(line, "search ") { + domains = strings.Fields(line[7:]) + } + } + return domains +} + +func getOptions(resolvConf string) []string { + options := []string{} + for _, line := range getLines(resolvConf) { + if strings.HasPrefix(line, "options ") { + options = strings.Fields(line[8:]) + } + } + return options +} From 82bd48a467fc85b583aa6e03c69a5c30df1edcfa Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Fri, 10 Apr 2020 11:28:24 -0400 Subject: [PATCH 06/18] drivers: apply dns config testing to java and docker drivers --- drivers/docker/driver_unix_test.go | 56 ++++++++++++++++++++++++++++++ drivers/exec/driver_unix_test.go | 8 +++++ drivers/java/driver_test.go | 53 ++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/drivers/docker/driver_unix_test.go b/drivers/docker/driver_unix_test.go index 690a69e7367..8aa362c94bd 100644 --- a/drivers/docker/driver_unix_test.go +++ b/drivers/docker/driver_unix_test.go @@ -805,3 +805,59 @@ func TestDocker_ExecTaskStreaming(t *testing.T) { dtestutil.ExecTaskStreamingConformanceTests(t, d, task.ID) } + +// Tests that a given DNSConfig properly configures dns +func Test_dnsConfig(t *testing.T) { + if !tu.IsCI() { + t.Parallel() + } + testutil.DockerCompatible(t) + require := require.New(t) + harness := dockerDriverHarness(t, nil) + defer harness.Kill() + + cases := []struct { + name string + cfg *drivers.DNSConfig + }{ + { + name: "nil DNSConfig", + }, + { + name: "basic", + cfg: &drivers.DNSConfig{ + Servers: []string{"1.1.1.1", "1.0.0.1"}, + }, + }, + { + name: "full", + cfg: &drivers.DNSConfig{ + Servers: []string{"1.1.1.1", "1.0.0.1"}, + Searches: []string{"local.test", "node.consul"}, + Options: []string{"ndots:2", "edns0"}, + }, + }, + } + + for _, c := range cases { + taskCfg := newTaskConfig("", []string{"/bin/sleep", "1000"}) + task := &drivers.TaskConfig{ + ID: uuid.Generate(), + Name: "nc-demo", + AllocID: uuid.Generate(), + Resources: basicResources, + DNS: c.cfg, + } + require.NoError(task.EncodeConcreteDriverConfig(&taskCfg)) + + cleanup := harness.MkAllocDir(task, false) + defer cleanup() + + _, _, err := harness.StartTask(task) + require.NoError(err) + defer harness.DestroyTask(task.ID, true) + + dtestutil.TestTaskDNSConfig(t, harness, task.ID, c.cfg) + } + +} diff --git a/drivers/exec/driver_unix_test.go b/drivers/exec/driver_unix_test.go index e6a59af4028..e14f90c5790 100644 --- a/drivers/exec/driver_unix_test.go +++ b/drivers/exec/driver_unix_test.go @@ -131,6 +131,14 @@ func TestExec_dnsConfig(t *testing.T) { Servers: []string{"1.1.1.1", "1.0.0.1"}, }, }, + { + name: "full", + cfg: &drivers.DNSConfig{ + Servers: []string{"1.1.1.1", "1.0.0.1"}, + Searches: []string{"local.test", "node.consul"}, + Options: []string{"ndots:2", "edns0"}, + }, + }, } for _, c := range cases { diff --git a/drivers/java/driver_test.go b/drivers/java/driver_test.go index b4b3d0010c3..3d952c21abe 100644 --- a/drivers/java/driver_test.go +++ b/drivers/java/driver_test.go @@ -345,3 +345,56 @@ config { require.EqualValues(t, expected, tc) } + +// Tests that a given DNSConfig properly configures dns +func Test_dnsConfig(t *testing.T) { + t.Parallel() + ctestutil.RequireRoot(t) + javaCompatible(t) + require := require.New(t) + d := NewDriver(testlog.HCLogger(t)) + harness := dtestutil.NewDriverHarness(t, d) + defer harness.Kill() + + cases := []struct { + name string + cfg *drivers.DNSConfig + }{ + { + name: "nil DNSConfig", + }, + { + name: "basic", + cfg: &drivers.DNSConfig{ + Servers: []string{"1.1.1.1", "1.0.0.1"}, + }, + }, + { + name: "full", + cfg: &drivers.DNSConfig{ + Servers: []string{"1.1.1.1", "1.0.0.1"}, + Searches: []string{"local.test", "node.consul"}, + Options: []string{"ndots:2", "edns0"}, + }, + }, + } + + for _, c := range cases { + tc := &TaskConfig{ + Class: "Hello", + Args: []string{"900"}, + } + task := basicTask(t, "demo-app", tc) + task.DNS = c.cfg + + cleanup := harness.MkAllocDir(task, false) + defer cleanup() + + _, _, err := harness.StartTask(task) + require.NoError(err) + defer d.DestroyTask(task.ID, true) + + dtestutil.TestTaskDNSConfig(t, harness, task.ID, c.cfg) + } + +} From 426dc29b03620350614c255e576a7b5c458bbdad Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Fri, 10 Apr 2020 13:52:40 -0400 Subject: [PATCH 07/18] driver: fix dns testing assertion --- plugins/drivers/testutils/dns_testing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/drivers/testutils/dns_testing.go b/plugins/drivers/testutils/dns_testing.go index 51afae2908b..318e8e750cf 100644 --- a/plugins/drivers/testutils/dns_testing.go +++ b/plugins/drivers/testutils/dns_testing.go @@ -30,7 +30,7 @@ func TestTaskDNSConfig(t *testing.T, driver *DriverHarness, taskID string, dns * if dns != nil { require.ElementsMatch(t, dns.Servers, getNameservers(resolvConf)) require.ElementsMatch(t, dns.Searches, getSearchDomains(resolvConf)) - require.ElementsMatch(t, dns.Searches, getOptions(resolvConf)) + require.ElementsMatch(t, dns.Options, getOptions(resolvConf)) } else { system, err := ioutil.ReadFile("/etc/resolv.conf") require.NoError(t, err) From ebed7e98024eabb2a6b158fe40a38d67d37de52e Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Mon, 20 Apr 2020 14:08:19 -0400 Subject: [PATCH 08/18] vendor: use libcontainer/resolvconf for parsing and building resolv.conf --- .../docker/libnetwork/resolvconf/README.md | 1 + .../libnetwork/resolvconf/dns/resolvconf.go | 26 + .../libnetwork/resolvconf/resolvconf.go | 285 +++++++ .../docker/libnetwork/types/types.go | 653 ++++++++++++++++ .../github.com/ishidawataru/sctp/GO_LICENSE | 27 + vendor/github.com/ishidawataru/sctp/LICENSE | 201 +++++ vendor/github.com/ishidawataru/sctp/NOTICE | 3 + vendor/github.com/ishidawataru/sctp/README.md | 18 + vendor/github.com/ishidawataru/sctp/go.mod | 3 + .../ishidawataru/sctp/ipsock_linux.go | 222 ++++++ vendor/github.com/ishidawataru/sctp/sctp.go | 729 ++++++++++++++++++ .../ishidawataru/sctp/sctp_linux.go | 305 ++++++++ .../ishidawataru/sctp/sctp_unsupported.go | 98 +++ vendor/vendor.json | 4 + 14 files changed, 2575 insertions(+) create mode 100644 vendor/github.com/docker/libnetwork/resolvconf/README.md create mode 100644 vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go create mode 100644 vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go create mode 100644 vendor/github.com/docker/libnetwork/types/types.go create mode 100644 vendor/github.com/ishidawataru/sctp/GO_LICENSE create mode 100644 vendor/github.com/ishidawataru/sctp/LICENSE create mode 100644 vendor/github.com/ishidawataru/sctp/NOTICE create mode 100644 vendor/github.com/ishidawataru/sctp/README.md create mode 100644 vendor/github.com/ishidawataru/sctp/go.mod create mode 100644 vendor/github.com/ishidawataru/sctp/ipsock_linux.go create mode 100644 vendor/github.com/ishidawataru/sctp/sctp.go create mode 100644 vendor/github.com/ishidawataru/sctp/sctp_linux.go create mode 100644 vendor/github.com/ishidawataru/sctp/sctp_unsupported.go diff --git a/vendor/github.com/docker/libnetwork/resolvconf/README.md b/vendor/github.com/docker/libnetwork/resolvconf/README.md new file mode 100644 index 00000000000..cdda554ba57 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/resolvconf/README.md @@ -0,0 +1 @@ +Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf diff --git a/vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go b/vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go new file mode 100644 index 00000000000..e348bc57f56 --- /dev/null +++ b/vendor/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go @@ -0,0 +1,26 @@ +package dns + +import ( + "regexp" +) + +// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range. +const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)` + +// IPv4Localhost is a regex pattern for IPv4 localhost address range. +const IPv4Localhost = `(127\.([0-9]{1,3}\.){2}[0-9]{1,3})` + +var localhostIPRegexp = regexp.MustCompile(IPLocalhost) +var localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost) + +// IsLocalhost returns true if ip matches the localhost IP regular expression. +// Used for determining if nameserver settings are being passed which are +// localhost addresses +func IsLocalhost(ip string) bool { + return localhostIPRegexp.MatchString(ip) +} + +// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression. +func IsIPv4Localhost(ip string) bool { + return localhostIPv4Regexp.MatchString(ip) +} diff --git a/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go b/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go new file mode 100644 index 00000000000..946bb87123e --- /dev/null +++ b/vendor/github.com/docker/libnetwork/resolvconf/resolvconf.go @@ -0,0 +1,285 @@ +// Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf +package resolvconf + +import ( + "bytes" + "io/ioutil" + "regexp" + "strings" + "sync" + + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/libnetwork/resolvconf/dns" + "github.com/docker/libnetwork/types" + "github.com/sirupsen/logrus" +) + +const ( + // defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path(). + defaultPath = "/etc/resolv.conf" + // alternatePath is a path different from defaultPath, that may be used to resolve DNS. See Path(). + alternatePath = "/run/systemd/resolve/resolv.conf" +) + +var ( + detectSystemdResolvConfOnce sync.Once + pathAfterSystemdDetection = defaultPath +) + +// Path returns the path to the resolv.conf file that libnetwork should use. +// +// When /etc/resolv.conf contains 127.0.0.53 as the only nameserver, then +// it is assumed systemd-resolved manages DNS. Because inside the container 127.0.0.53 +// is not a valid DNS server, Path() returns /run/systemd/resolve/resolv.conf +// which is the resolv.conf that systemd-resolved generates and manages. +// Otherwise Path() returns /etc/resolv.conf. +// +// Errors are silenced as they will inevitably resurface at future open/read calls. +// +// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf +func Path() string { + detectSystemdResolvConfOnce.Do(func() { + candidateResolvConf, err := ioutil.ReadFile(defaultPath) + if err != nil { + // silencing error as it will resurface at next calls trying to read defaultPath + return + } + ns := GetNameservers(candidateResolvConf, types.IP) + if len(ns) == 1 && ns[0] == "127.0.0.53" { + pathAfterSystemdDetection = alternatePath + logrus.Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath) + } + }) + return pathAfterSystemdDetection +} + +var ( + // Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS + defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"} + defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"} + ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)` + ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock + // This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also + // will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants + // -- e.g. other link-local types -- either won't work in containers or are unnecessary. + // For readability and sufficiency for Docker purposes this seemed more reasonable than a + // 1000+ character regexp with exact and complete IPv6 validation + ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})(%\w+)?` + + localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`) + nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`) + nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`) + nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`) + nsIPv4Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `))\s*$`) + searchRegexp = regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) + optionsRegexp = regexp.MustCompile(`^\s*options\s*(([^\s]+\s*)*)$`) +) + +var lastModified struct { + sync.Mutex + sha256 string + contents []byte +} + +// File contains the resolv.conf content and its hash +type File struct { + Content []byte + Hash string +} + +// Get returns the contents of /etc/resolv.conf and its hash +func Get() (*File, error) { + return GetSpecific(Path()) +} + +// GetSpecific returns the contents of the user specified resolv.conf file and its hash +func GetSpecific(path string) (*File, error) { + resolv, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + hash, err := ioutils.HashData(bytes.NewReader(resolv)) + if err != nil { + return nil, err + } + return &File{Content: resolv, Hash: hash}, nil +} + +// GetIfChanged retrieves the host /etc/resolv.conf file, checks against the last hash +// and, if modified since last check, returns the bytes and new hash. +// This feature is used by the resolv.conf updater for containers +func GetIfChanged() (*File, error) { + lastModified.Lock() + defer lastModified.Unlock() + + resolv, err := ioutil.ReadFile(Path()) + if err != nil { + return nil, err + } + newHash, err := ioutils.HashData(bytes.NewReader(resolv)) + if err != nil { + return nil, err + } + if lastModified.sha256 != newHash { + lastModified.sha256 = newHash + lastModified.contents = resolv + return &File{Content: resolv, Hash: newHash}, nil + } + // nothing changed, so return no data + return nil, nil +} + +// GetLastModified retrieves the last used contents and hash of the host resolv.conf. +// Used by containers updating on restart +func GetLastModified() *File { + lastModified.Lock() + defer lastModified.Unlock() + + return &File{Content: lastModified.contents, Hash: lastModified.sha256} +} + +// FilterResolvDNS cleans up the config in resolvConf. It has two main jobs: +// 1. It looks for localhost (127.*|::1) entries in the provided +// resolv.conf, removing local nameserver entries, and, if the resulting +// cleaned config has no defined nameservers left, adds default DNS entries +// 2. Given the caller provides the enable/disable state of IPv6, the filter +// code will remove all IPv6 nameservers if it is not enabled for containers +// +func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) { + cleanedResolvConf := localhostNSRegexp.ReplaceAll(resolvConf, []byte{}) + // if IPv6 is not enabled, also clean out any IPv6 address nameserver + if !ipv6Enabled { + cleanedResolvConf = nsIPv6Regexp.ReplaceAll(cleanedResolvConf, []byte{}) + } + // if the resulting resolvConf has no more nameservers defined, add appropriate + // default DNS servers for IPv4 and (optionally) IPv6 + if len(GetNameservers(cleanedResolvConf, types.IP)) == 0 { + logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns) + dns := defaultIPv4Dns + if ipv6Enabled { + logrus.Infof("IPv6 enabled; Adding default IPv6 external servers: %v", defaultIPv6Dns) + dns = append(dns, defaultIPv6Dns...) + } + cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...) + } + hash, err := ioutils.HashData(bytes.NewReader(cleanedResolvConf)) + if err != nil { + return nil, err + } + return &File{Content: cleanedResolvConf, Hash: hash}, nil +} + +// getLines parses input into lines and strips away comments. +func getLines(input []byte, commentMarker []byte) [][]byte { + lines := bytes.Split(input, []byte("\n")) + var output [][]byte + for _, currentLine := range lines { + var commentIndex = bytes.Index(currentLine, commentMarker) + if commentIndex == -1 { + output = append(output, currentLine) + } else { + output = append(output, currentLine[:commentIndex]) + } + } + return output +} + +// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf +func GetNameservers(resolvConf []byte, kind int) []string { + nameservers := []string{} + for _, line := range getLines(resolvConf, []byte("#")) { + var ns [][]byte + if kind == types.IP { + ns = nsRegexp.FindSubmatch(line) + } else if kind == types.IPv4 { + ns = nsIPv4Regexpmatch.FindSubmatch(line) + } else if kind == types.IPv6 { + ns = nsIPv6Regexpmatch.FindSubmatch(line) + } + if len(ns) > 0 { + nameservers = append(nameservers, string(ns[1])) + } + } + return nameservers +} + +// GetNameserversAsCIDR returns nameservers (if any) listed in +// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") +// This function's output is intended for net.ParseCIDR +func GetNameserversAsCIDR(resolvConf []byte) []string { + nameservers := []string{} + for _, nameserver := range GetNameservers(resolvConf, types.IP) { + var address string + // If IPv6, strip zone if present + if strings.Contains(nameserver, ":") { + address = strings.Split(nameserver, "%")[0] + "/128" + } else { + address = nameserver + "/32" + } + nameservers = append(nameservers, address) + } + return nameservers +} + +// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf +// If more than one search line is encountered, only the contents of the last +// one is returned. +func GetSearchDomains(resolvConf []byte) []string { + domains := []string{} + for _, line := range getLines(resolvConf, []byte("#")) { + match := searchRegexp.FindSubmatch(line) + if match == nil { + continue + } + domains = strings.Fields(string(match[1])) + } + return domains +} + +// GetOptions returns options (if any) listed in /etc/resolv.conf +// If more than one options line is encountered, only the contents of the last +// one is returned. +func GetOptions(resolvConf []byte) []string { + options := []string{} + for _, line := range getLines(resolvConf, []byte("#")) { + match := optionsRegexp.FindSubmatch(line) + if match == nil { + continue + } + options = strings.Fields(string(match[1])) + } + return options +} + +// Build writes a configuration file to path containing a "nameserver" entry +// for every element in dns, a "search" entry for every element in +// dnsSearch, and an "options" entry for every element in dnsOptions. +func Build(path string, dns, dnsSearch, dnsOptions []string) (*File, error) { + content := bytes.NewBuffer(nil) + if len(dnsSearch) > 0 { + if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." { + if _, err := content.WriteString("search " + searchString + "\n"); err != nil { + return nil, err + } + } + } + for _, dns := range dns { + if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil { + return nil, err + } + } + if len(dnsOptions) > 0 { + if optsString := strings.Join(dnsOptions, " "); strings.Trim(optsString, " ") != "" { + if _, err := content.WriteString("options " + optsString + "\n"); err != nil { + return nil, err + } + } + } + + hash, err := ioutils.HashData(bytes.NewReader(content.Bytes())) + if err != nil { + return nil, err + } + + return &File{Content: content.Bytes(), Hash: hash}, ioutil.WriteFile(path, content.Bytes(), 0644) +} diff --git a/vendor/github.com/docker/libnetwork/types/types.go b/vendor/github.com/docker/libnetwork/types/types.go new file mode 100644 index 00000000000..db1960c104e --- /dev/null +++ b/vendor/github.com/docker/libnetwork/types/types.go @@ -0,0 +1,653 @@ +// Package types contains types that are common across libnetwork project +package types + +import ( + "bytes" + "fmt" + "net" + "strconv" + "strings" + + "github.com/ishidawataru/sctp" +) + +// constants for the IP address type +const ( + IP = iota // IPv4 and IPv6 + IPv4 + IPv6 +) + +// EncryptionKey is the libnetwork representation of the key distributed by the lead +// manager. +type EncryptionKey struct { + Subsystem string + Algorithm int32 + Key []byte + LamportTime uint64 +} + +// UUID represents a globally unique ID of various resources like network and endpoint +type UUID string + +// QosPolicy represents a quality of service policy on an endpoint +type QosPolicy struct { + MaxEgressBandwidth uint64 +} + +// TransportPort represents a local Layer 4 endpoint +type TransportPort struct { + Proto Protocol + Port uint16 +} + +// Equal checks if this instance of Transportport is equal to the passed one +func (t *TransportPort) Equal(o *TransportPort) bool { + if t == o { + return true + } + + if o == nil { + return false + } + + if t.Proto != o.Proto || t.Port != o.Port { + return false + } + + return true +} + +// GetCopy returns a copy of this TransportPort structure instance +func (t *TransportPort) GetCopy() TransportPort { + return TransportPort{Proto: t.Proto, Port: t.Port} +} + +// String returns the TransportPort structure in string form +func (t *TransportPort) String() string { + return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port) +} + +// FromString reads the TransportPort structure from string +func (t *TransportPort) FromString(s string) error { + ps := strings.Split(s, "/") + if len(ps) == 2 { + t.Proto = ParseProtocol(ps[0]) + if p, err := strconv.ParseUint(ps[1], 10, 16); err == nil { + t.Port = uint16(p) + return nil + } + } + return BadRequestErrorf("invalid format for transport port: %s", s) +} + +// PortBinding represents a port binding between the container and the host +type PortBinding struct { + Proto Protocol + IP net.IP + Port uint16 + HostIP net.IP + HostPort uint16 + HostPortEnd uint16 +} + +// HostAddr returns the host side transport address +func (p PortBinding) HostAddr() (net.Addr, error) { + switch p.Proto { + case UDP: + return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil + case TCP: + return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil + case SCTP: + return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.HostIP}}, Port: int(p.HostPort)}, nil + default: + return nil, ErrInvalidProtocolBinding(p.Proto.String()) + } +} + +// ContainerAddr returns the container side transport address +func (p PortBinding) ContainerAddr() (net.Addr, error) { + switch p.Proto { + case UDP: + return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil + case TCP: + return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil + case SCTP: + return &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: p.IP}}, Port: int(p.Port)}, nil + default: + return nil, ErrInvalidProtocolBinding(p.Proto.String()) + } +} + +// GetCopy returns a copy of this PortBinding structure instance +func (p *PortBinding) GetCopy() PortBinding { + return PortBinding{ + Proto: p.Proto, + IP: GetIPCopy(p.IP), + Port: p.Port, + HostIP: GetIPCopy(p.HostIP), + HostPort: p.HostPort, + HostPortEnd: p.HostPortEnd, + } +} + +// String returns the PortBinding structure in string form +func (p *PortBinding) String() string { + ret := fmt.Sprintf("%s/", p.Proto) + if p.IP != nil { + ret += p.IP.String() + } + ret = fmt.Sprintf("%s:%d/", ret, p.Port) + if p.HostIP != nil { + ret += p.HostIP.String() + } + ret = fmt.Sprintf("%s:%d", ret, p.HostPort) + return ret +} + +// FromString reads the PortBinding structure from string s. +// String s is a triple of "protocol/containerIP:port/hostIP:port" +// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form. +// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported. +// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString +// returns an error. +func (p *PortBinding) FromString(s string) error { + ps := strings.Split(s, "/") + if len(ps) != 3 { + return BadRequestErrorf("invalid format for port binding: %s", s) + } + + p.Proto = ParseProtocol(ps[0]) + + var err error + if p.IP, p.Port, err = parseIPPort(ps[1]); err != nil { + return BadRequestErrorf("failed to parse Container IP/Port in port binding: %s", err.Error()) + } + + if p.HostIP, p.HostPort, err = parseIPPort(ps[2]); err != nil { + return BadRequestErrorf("failed to parse Host IP/Port in port binding: %s", err.Error()) + } + + return nil +} + +func parseIPPort(s string) (net.IP, uint16, error) { + hoststr, portstr, err := net.SplitHostPort(s) + if err != nil { + return nil, 0, err + } + + ip := net.ParseIP(hoststr) + if ip == nil { + return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr) + } + + port, err := strconv.ParseUint(portstr, 10, 16) + if err != nil { + return nil, 0, BadRequestErrorf("invalid port: %s", portstr) + } + + return ip, uint16(port), nil +} + +// Equal checks if this instance of PortBinding is equal to the passed one +func (p *PortBinding) Equal(o *PortBinding) bool { + if p == o { + return true + } + + if o == nil { + return false + } + + if p.Proto != o.Proto || p.Port != o.Port || + p.HostPort != o.HostPort || p.HostPortEnd != o.HostPortEnd { + return false + } + + if p.IP != nil { + if !p.IP.Equal(o.IP) { + return false + } + } else { + if o.IP != nil { + return false + } + } + + if p.HostIP != nil { + if !p.HostIP.Equal(o.HostIP) { + return false + } + } else { + if o.HostIP != nil { + return false + } + } + + return true +} + +// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid. +type ErrInvalidProtocolBinding string + +func (ipb ErrInvalidProtocolBinding) Error() string { + return fmt.Sprintf("invalid transport protocol: %s", string(ipb)) +} + +const ( + // ICMP is for the ICMP ip protocol + ICMP = 1 + // TCP is for the TCP ip protocol + TCP = 6 + // UDP is for the UDP ip protocol + UDP = 17 + // SCTP is for the SCTP ip protocol + SCTP = 132 +) + +// Protocol represents an IP protocol number +type Protocol uint8 + +func (p Protocol) String() string { + switch p { + case ICMP: + return "icmp" + case TCP: + return "tcp" + case UDP: + return "udp" + case SCTP: + return "sctp" + default: + return fmt.Sprintf("%d", p) + } +} + +// ParseProtocol returns the respective Protocol type for the passed string +func ParseProtocol(s string) Protocol { + switch strings.ToLower(s) { + case "icmp": + return ICMP + case "udp": + return UDP + case "tcp": + return TCP + case "sctp": + return SCTP + default: + return 0 + } +} + +// GetMacCopy returns a copy of the passed MAC address +func GetMacCopy(from net.HardwareAddr) net.HardwareAddr { + if from == nil { + return nil + } + to := make(net.HardwareAddr, len(from)) + copy(to, from) + return to +} + +// GetIPCopy returns a copy of the passed IP address +func GetIPCopy(from net.IP) net.IP { + if from == nil { + return nil + } + to := make(net.IP, len(from)) + copy(to, from) + return to +} + +// GetIPNetCopy returns a copy of the passed IP Network +func GetIPNetCopy(from *net.IPNet) *net.IPNet { + if from == nil { + return nil + } + bm := make(net.IPMask, len(from.Mask)) + copy(bm, from.Mask) + return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm} +} + +// GetIPNetCanonical returns the canonical form for the passed network +func GetIPNetCanonical(nw *net.IPNet) *net.IPNet { + if nw == nil { + return nil + } + c := GetIPNetCopy(nw) + c.IP = c.IP.Mask(nw.Mask) + return c +} + +// CompareIPNet returns equal if the two IP Networks are equal +func CompareIPNet(a, b *net.IPNet) bool { + if a == b { + return true + } + if a == nil || b == nil { + return false + } + return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask) +} + +// GetMinimalIP returns the address in its shortest form +// If ip contains an IPv4-mapped IPv6 address, the 4-octet form of the IPv4 address will be returned. +// Otherwise ip is returned unchanged. +func GetMinimalIP(ip net.IP) net.IP { + if ip != nil && ip.To4() != nil { + return ip.To4() + } + return ip +} + +// GetMinimalIPNet returns a copy of the passed IP Network with congruent ip and mask notation +func GetMinimalIPNet(nw *net.IPNet) *net.IPNet { + if nw == nil { + return nil + } + if len(nw.IP) == 16 && nw.IP.To4() != nil { + m := nw.Mask + if len(m) == 16 { + m = m[12:16] + } + return &net.IPNet{IP: nw.IP.To4(), Mask: m} + } + return nw +} + +// IsIPNetValid returns true if the ipnet is a valid network/mask +// combination. Otherwise returns false. +func IsIPNetValid(nw *net.IPNet) bool { + return nw.String() != "0.0.0.0/0" +} + +var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + +// compareIPMask checks if the passed ip and mask are semantically compatible. +// It returns the byte indexes for the address and mask so that caller can +// do bitwise operations without modifying address representation. +func compareIPMask(ip net.IP, mask net.IPMask) (is int, ms int, err error) { + // Find the effective starting of address and mask + if len(ip) == net.IPv6len && ip.To4() != nil { + is = 12 + } + if len(ip[is:]) == net.IPv4len && len(mask) == net.IPv6len && bytes.Equal(mask[:12], v4inV6MaskPrefix) { + ms = 12 + } + // Check if address and mask are semantically compatible + if len(ip[is:]) != len(mask[ms:]) { + err = fmt.Errorf("ip and mask are not compatible: (%#v, %#v)", ip, mask) + } + return +} + +// GetHostPartIP returns the host portion of the ip address identified by the mask. +// IP address representation is not modified. If address and mask are not compatible +// an error is returned. +func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) { + // Find the effective starting of address and mask + is, ms, err := compareIPMask(ip, mask) + if err != nil { + return nil, fmt.Errorf("cannot compute host portion ip address because %s", err) + } + + // Compute host portion + out := GetIPCopy(ip) + for i := 0; i < len(mask[ms:]); i++ { + out[is+i] &= ^mask[ms+i] + } + + return out, nil +} + +// GetBroadcastIP returns the broadcast ip address for the passed network (ip and mask). +// IP address representation is not modified. If address and mask are not compatible +// an error is returned. +func GetBroadcastIP(ip net.IP, mask net.IPMask) (net.IP, error) { + // Find the effective starting of address and mask + is, ms, err := compareIPMask(ip, mask) + if err != nil { + return nil, fmt.Errorf("cannot compute broadcast ip address because %s", err) + } + + // Compute broadcast address + out := GetIPCopy(ip) + for i := 0; i < len(mask[ms:]); i++ { + out[is+i] |= ^mask[ms+i] + } + + return out, nil +} + +// ParseCIDR returns the *net.IPNet represented by the passed CIDR notation +func ParseCIDR(cidr string) (n *net.IPNet, e error) { + var i net.IP + if i, n, e = net.ParseCIDR(cidr); e == nil { + n.IP = i + } + return +} + +const ( + // NEXTHOP indicates a StaticRoute with an IP next hop. + NEXTHOP = iota + + // CONNECTED indicates a StaticRoute with an interface for directly connected peers. + CONNECTED +) + +// StaticRoute is a statically-provisioned IP route. +type StaticRoute struct { + Destination *net.IPNet + + RouteType int // NEXT_HOP or CONNECTED + + // NextHop will be resolved by the kernel (i.e. as a loose hop). + NextHop net.IP +} + +// GetCopy returns a copy of this StaticRoute structure +func (r *StaticRoute) GetCopy() *StaticRoute { + d := GetIPNetCopy(r.Destination) + nh := GetIPCopy(r.NextHop) + return &StaticRoute{Destination: d, + RouteType: r.RouteType, + NextHop: nh, + } +} + +// InterfaceStatistics represents the interface's statistics +type InterfaceStatistics struct { + RxBytes uint64 + RxPackets uint64 + RxErrors uint64 + RxDropped uint64 + TxBytes uint64 + TxPackets uint64 + TxErrors uint64 + TxDropped uint64 +} + +func (is *InterfaceStatistics) String() string { + return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d", + is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped) +} + +/****************************** + * Well-known Error Interfaces + ******************************/ + +// MaskableError is an interface for errors which can be ignored by caller +type MaskableError interface { + // Maskable makes implementer into MaskableError type + Maskable() +} + +// RetryError is an interface for errors which might get resolved through retry +type RetryError interface { + // Retry makes implementer into RetryError type + Retry() +} + +// BadRequestError is an interface for errors originated by a bad request +type BadRequestError interface { + // BadRequest makes implementer into BadRequestError type + BadRequest() +} + +// NotFoundError is an interface for errors raised because a needed resource is not available +type NotFoundError interface { + // NotFound makes implementer into NotFoundError type + NotFound() +} + +// ForbiddenError is an interface for errors which denote a valid request that cannot be honored +type ForbiddenError interface { + // Forbidden makes implementer into ForbiddenError type + Forbidden() +} + +// NoServiceError is an interface for errors returned when the required service is not available +type NoServiceError interface { + // NoService makes implementer into NoServiceError type + NoService() +} + +// TimeoutError is an interface for errors raised because of timeout +type TimeoutError interface { + // Timeout makes implementer into TimeoutError type + Timeout() +} + +// NotImplementedError is an interface for errors raised because of requested functionality is not yet implemented +type NotImplementedError interface { + // NotImplemented makes implementer into NotImplementedError type + NotImplemented() +} + +// InternalError is an interface for errors raised because of an internal error +type InternalError interface { + // Internal makes implementer into InternalError type + Internal() +} + +/****************************** + * Well-known Error Formatters + ******************************/ + +// BadRequestErrorf creates an instance of BadRequestError +func BadRequestErrorf(format string, params ...interface{}) error { + return badRequest(fmt.Sprintf(format, params...)) +} + +// NotFoundErrorf creates an instance of NotFoundError +func NotFoundErrorf(format string, params ...interface{}) error { + return notFound(fmt.Sprintf(format, params...)) +} + +// ForbiddenErrorf creates an instance of ForbiddenError +func ForbiddenErrorf(format string, params ...interface{}) error { + return forbidden(fmt.Sprintf(format, params...)) +} + +// NoServiceErrorf creates an instance of NoServiceError +func NoServiceErrorf(format string, params ...interface{}) error { + return noService(fmt.Sprintf(format, params...)) +} + +// NotImplementedErrorf creates an instance of NotImplementedError +func NotImplementedErrorf(format string, params ...interface{}) error { + return notImpl(fmt.Sprintf(format, params...)) +} + +// TimeoutErrorf creates an instance of TimeoutError +func TimeoutErrorf(format string, params ...interface{}) error { + return timeout(fmt.Sprintf(format, params...)) +} + +// InternalErrorf creates an instance of InternalError +func InternalErrorf(format string, params ...interface{}) error { + return internal(fmt.Sprintf(format, params...)) +} + +// InternalMaskableErrorf creates an instance of InternalError and MaskableError +func InternalMaskableErrorf(format string, params ...interface{}) error { + return maskInternal(fmt.Sprintf(format, params...)) +} + +// RetryErrorf creates an instance of RetryError +func RetryErrorf(format string, params ...interface{}) error { + return retry(fmt.Sprintf(format, params...)) +} + +/*********************** + * Internal Error Types + ***********************/ +type badRequest string + +func (br badRequest) Error() string { + return string(br) +} +func (br badRequest) BadRequest() {} + +type maskBadRequest string + +type notFound string + +func (nf notFound) Error() string { + return string(nf) +} +func (nf notFound) NotFound() {} + +type forbidden string + +func (frb forbidden) Error() string { + return string(frb) +} +func (frb forbidden) Forbidden() {} + +type noService string + +func (ns noService) Error() string { + return string(ns) +} +func (ns noService) NoService() {} + +type maskNoService string + +type timeout string + +func (to timeout) Error() string { + return string(to) +} +func (to timeout) Timeout() {} + +type notImpl string + +func (ni notImpl) Error() string { + return string(ni) +} +func (ni notImpl) NotImplemented() {} + +type internal string + +func (nt internal) Error() string { + return string(nt) +} +func (nt internal) Internal() {} + +type maskInternal string + +func (mnt maskInternal) Error() string { + return string(mnt) +} +func (mnt maskInternal) Internal() {} +func (mnt maskInternal) Maskable() {} + +type retry string + +func (r retry) Error() string { + return string(r) +} +func (r retry) Retry() {} diff --git a/vendor/github.com/ishidawataru/sctp/GO_LICENSE b/vendor/github.com/ishidawataru/sctp/GO_LICENSE new file mode 100644 index 00000000000..6a66aea5eaf --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/GO_LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/ishidawataru/sctp/LICENSE b/vendor/github.com/ishidawataru/sctp/LICENSE new file mode 100644 index 00000000000..8dada3edaf5 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/ishidawataru/sctp/NOTICE b/vendor/github.com/ishidawataru/sctp/NOTICE new file mode 100644 index 00000000000..cfb675fd4ba --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/NOTICE @@ -0,0 +1,3 @@ +This source code includes following third party code + +- ipsock_linux.go : licensed by the Go authors, see GO_LICENSE file for the license which applies to the code diff --git a/vendor/github.com/ishidawataru/sctp/README.md b/vendor/github.com/ishidawataru/sctp/README.md new file mode 100644 index 00000000000..574ececa862 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/README.md @@ -0,0 +1,18 @@ +Stream Control Transmission Protocol (SCTP) +---- + +[![Build Status](https://travis-ci.org/ishidawataru/sctp.svg?branch=master)](https://travis-ci.org/ishidawataru/sctp/builds) + +Examples +---- + +See `example/sctp.go` + +```go +$ cd example +$ go build +$ # run example SCTP server +$ ./example -server -port 1000 -ip 10.10.0.1,10.20.0.1 +$ # run example SCTP client +$ ./example -port 1000 -ip 10.10.0.1,10.20.0.1 +``` diff --git a/vendor/github.com/ishidawataru/sctp/go.mod b/vendor/github.com/ishidawataru/sctp/go.mod new file mode 100644 index 00000000000..5adf982b086 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/go.mod @@ -0,0 +1,3 @@ +module github.com/ishidawataru/sctp + +go 1.12 diff --git a/vendor/github.com/ishidawataru/sctp/ipsock_linux.go b/vendor/github.com/ishidawataru/sctp/ipsock_linux.go new file mode 100644 index 00000000000..3df30fa4601 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/ipsock_linux.go @@ -0,0 +1,222 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the GO_LICENSE file. + +package sctp + +import ( + "net" + "os" + "sync" + "syscall" +) + +//from https://github.com/golang/go +// Boolean to int. +func boolint(b bool) int { + if b { + return 1 + } + return 0 +} + +//from https://github.com/golang/go +func ipToSockaddr(family int, ip net.IP, port int, zone string) (syscall.Sockaddr, error) { + switch family { + case syscall.AF_INET: + if len(ip) == 0 { + ip = net.IPv4zero + } + ip4 := ip.To4() + if ip4 == nil { + return nil, &net.AddrError{Err: "non-IPv4 address", Addr: ip.String()} + } + sa := &syscall.SockaddrInet4{Port: port} + copy(sa.Addr[:], ip4) + return sa, nil + case syscall.AF_INET6: + // In general, an IP wildcard address, which is either + // "0.0.0.0" or "::", means the entire IP addressing + // space. For some historical reason, it is used to + // specify "any available address" on some operations + // of IP node. + // + // When the IP node supports IPv4-mapped IPv6 address, + // we allow an listener to listen to the wildcard + // address of both IP addressing spaces by specifying + // IPv6 wildcard address. + if len(ip) == 0 || ip.Equal(net.IPv4zero) { + ip = net.IPv6zero + } + // We accept any IPv6 address including IPv4-mapped + // IPv6 address. + ip6 := ip.To16() + if ip6 == nil { + return nil, &net.AddrError{Err: "non-IPv6 address", Addr: ip.String()} + } + //we set ZoneId to 0, as currently we use this functon only to probe the IP capabilities of the host + //if real Zone handling is required, the zone cache implementation in golang/net should be pulled here + sa := &syscall.SockaddrInet6{Port: port, ZoneId: 0} + copy(sa.Addr[:], ip6) + return sa, nil + } + return nil, &net.AddrError{Err: "invalid address family", Addr: ip.String()} +} + +//from https://github.com/golang/go +func sockaddr(a *net.TCPAddr, family int) (syscall.Sockaddr, error) { + if a == nil { + return nil, nil + } + return ipToSockaddr(family, a.IP, a.Port, a.Zone) +} + +//from https://github.com/golang/go +type ipStackCapabilities struct { + sync.Once // guards following + ipv4Enabled bool + ipv6Enabled bool + ipv4MappedIPv6Enabled bool +} + +//from https://github.com/golang/go +var ipStackCaps ipStackCapabilities + +//from https://github.com/golang/go +// supportsIPv4 reports whether the platform supports IPv4 networking +// functionality. +func supportsIPv4() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv4Enabled +} + +//from https://github.com/golang/go +// supportsIPv6 reports whether the platform supports IPv6 networking +// functionality. +func supportsIPv6() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv6Enabled +} + +//from https://github.com/golang/go +// supportsIPv4map reports whether the platform supports mapping an +// IPv4 address inside an IPv6 address at transport layer +// protocols. See RFC 4291, RFC 4038 and RFC 3493. +func supportsIPv4map() bool { + ipStackCaps.Once.Do(ipStackCaps.probe) + return ipStackCaps.ipv4MappedIPv6Enabled +} + +//from https://github.com/golang/go +// Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication +// capabilities which are controlled by the IPV6_V6ONLY socket option +// and kernel configuration. +// +// Should we try to use the IPv4 socket interface if we're only +// dealing with IPv4 sockets? As long as the host system understands +// IPv4-mapped IPv6, it's okay to pass IPv4-mapeed IPv6 addresses to +// the IPv6 interface. That simplifies our code and is most +// general. Unfortunately, we need to run on kernels built without +// IPv6 support too. So probe the kernel to figure it out. +func (p *ipStackCapabilities) probe() { + s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + switch err { + case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: + case nil: + syscall.Close(s) + p.ipv4Enabled = true + } + var probes = []struct { + laddr net.TCPAddr + value int + }{ + // IPv6 communication capability + {laddr: net.TCPAddr{IP: net.IPv6loopback}, value: 1}, + // IPv4-mapped IPv6 address communication capability + {laddr: net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)}, value: 0}, + } + + for i := range probes { + s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + if err != nil { + continue + } + defer syscall.Close(s) + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value) + sa, err := sockaddr(&(probes[i].laddr), syscall.AF_INET6) + if err != nil { + continue + } + if err := syscall.Bind(s, sa); err != nil { + continue + } + if i == 0 { + p.ipv6Enabled = true + } else { + p.ipv4MappedIPv6Enabled = true + } + } +} + +//from https://github.com/golang/go +//Change: we check the first IP address in the list of candidate SCTP IP addresses +func (a *SCTPAddr) isWildcard() bool { + if a == nil { + return true + } + if 0 == len(a.IPAddrs) { + return true + } + + return a.IPAddrs[0].IP.IsUnspecified() +} + +func (a *SCTPAddr) family() int { + if a != nil { + for _, ip := range a.IPAddrs { + if ip.IP.To4() == nil { + return syscall.AF_INET6 + } + } + } + return syscall.AF_INET +} + +//from https://github.com/golang/go +func favoriteAddrFamily(network string, laddr *SCTPAddr, raddr *SCTPAddr, mode string) (family int, ipv6only bool) { + switch network[len(network)-1] { + case '4': + return syscall.AF_INET, false + case '6': + return syscall.AF_INET6, true + } + + if mode == "listen" && (laddr == nil || laddr.isWildcard()) { + if supportsIPv4map() || !supportsIPv4() { + return syscall.AF_INET6, false + } + if laddr == nil { + return syscall.AF_INET, false + } + return laddr.family(), false + } + + if (laddr == nil || laddr.family() == syscall.AF_INET) && + (raddr == nil || raddr.family() == syscall.AF_INET) { + return syscall.AF_INET, false + } + return syscall.AF_INET6, false +} + +//from https://github.com/golang/go +//Changes: it is for SCTP only +func setDefaultSockopts(s int, family int, ipv6only bool) error { + if family == syscall.AF_INET6 { + // Allow both IP versions even if the OS default + // is otherwise. Note that some operating systems + // never admit this option. + syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only)) + } + // Allow broadcast. + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)) +} diff --git a/vendor/github.com/ishidawataru/sctp/sctp.go b/vendor/github.com/ishidawataru/sctp/sctp.go new file mode 100644 index 00000000000..94842f42702 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/sctp.go @@ -0,0 +1,729 @@ +// Copyright 2019 Wataru Ishida. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sctp + +import ( + "bytes" + "encoding/binary" + "fmt" + "net" + "strconv" + "strings" + "sync" + "sync/atomic" + "syscall" + "time" + "unsafe" +) + +const ( + SOL_SCTP = 132 + + SCTP_BINDX_ADD_ADDR = 0x01 + SCTP_BINDX_REM_ADDR = 0x02 + + MSG_NOTIFICATION = 0x8000 +) + +const ( + SCTP_RTOINFO = iota + SCTP_ASSOCINFO + SCTP_INITMSG + SCTP_NODELAY + SCTP_AUTOCLOSE + SCTP_SET_PEER_PRIMARY_ADDR + SCTP_PRIMARY_ADDR + SCTP_ADAPTATION_LAYER + SCTP_DISABLE_FRAGMENTS + SCTP_PEER_ADDR_PARAMS + SCTP_DEFAULT_SENT_PARAM + SCTP_EVENTS + SCTP_I_WANT_MAPPED_V4_ADDR + SCTP_MAXSEG + SCTP_STATUS + SCTP_GET_PEER_ADDR_INFO + SCTP_DELAYED_ACK_TIME + SCTP_DELAYED_ACK = SCTP_DELAYED_ACK_TIME + SCTP_DELAYED_SACK = SCTP_DELAYED_ACK_TIME + + SCTP_SOCKOPT_BINDX_ADD = 100 + SCTP_SOCKOPT_BINDX_REM = 101 + SCTP_SOCKOPT_PEELOFF = 102 + SCTP_GET_PEER_ADDRS = 108 + SCTP_GET_LOCAL_ADDRS = 109 + SCTP_SOCKOPT_CONNECTX = 110 + SCTP_SOCKOPT_CONNECTX3 = 111 +) + +const ( + SCTP_EVENT_DATA_IO = 1 << iota + SCTP_EVENT_ASSOCIATION + SCTP_EVENT_ADDRESS + SCTP_EVENT_SEND_FAILURE + SCTP_EVENT_PEER_ERROR + SCTP_EVENT_SHUTDOWN + SCTP_EVENT_PARTIAL_DELIVERY + SCTP_EVENT_ADAPTATION_LAYER + SCTP_EVENT_AUTHENTICATION + SCTP_EVENT_SENDER_DRY + + SCTP_EVENT_ALL = SCTP_EVENT_DATA_IO | SCTP_EVENT_ASSOCIATION | SCTP_EVENT_ADDRESS | SCTP_EVENT_SEND_FAILURE | SCTP_EVENT_PEER_ERROR | SCTP_EVENT_SHUTDOWN | SCTP_EVENT_PARTIAL_DELIVERY | SCTP_EVENT_ADAPTATION_LAYER | SCTP_EVENT_AUTHENTICATION | SCTP_EVENT_SENDER_DRY +) + +type SCTPNotificationType int + +const ( + SCTP_SN_TYPE_BASE = SCTPNotificationType(iota + (1 << 15)) + SCTP_ASSOC_CHANGE + SCTP_PEER_ADDR_CHANGE + SCTP_SEND_FAILED + SCTP_REMOTE_ERROR + SCTP_SHUTDOWN_EVENT + SCTP_PARTIAL_DELIVERY_EVENT + SCTP_ADAPTATION_INDICATION + SCTP_AUTHENTICATION_INDICATION + SCTP_SENDER_DRY_EVENT +) + +type NotificationHandler func([]byte) error + +type EventSubscribe struct { + DataIO uint8 + Association uint8 + Address uint8 + SendFailure uint8 + PeerError uint8 + Shutdown uint8 + PartialDelivery uint8 + AdaptationLayer uint8 + Authentication uint8 + SenderDry uint8 +} + +const ( + SCTP_CMSG_INIT = iota + SCTP_CMSG_SNDRCV + SCTP_CMSG_SNDINFO + SCTP_CMSG_RCVINFO + SCTP_CMSG_NXTINFO +) + +const ( + SCTP_UNORDERED = 1 << iota + SCTP_ADDR_OVER + SCTP_ABORT + SCTP_SACK_IMMEDIATELY + SCTP_EOF +) + +const ( + SCTP_MAX_STREAM = 0xffff +) + +type InitMsg struct { + NumOstreams uint16 + MaxInstreams uint16 + MaxAttempts uint16 + MaxInitTimeout uint16 +} + +type SndRcvInfo struct { + Stream uint16 + SSN uint16 + Flags uint16 + _ uint16 + PPID uint32 + Context uint32 + TTL uint32 + TSN uint32 + CumTSN uint32 + AssocID int32 +} + +type SndInfo struct { + SID uint16 + Flags uint16 + PPID uint32 + Context uint32 + AssocID int32 +} + +type GetAddrsOld struct { + AssocID int32 + AddrNum int32 + Addrs uintptr +} + +type NotificationHeader struct { + Type uint16 + Flags uint16 + Length uint32 +} + +type SCTPState uint16 + +const ( + SCTP_COMM_UP = SCTPState(iota) + SCTP_COMM_LOST + SCTP_RESTART + SCTP_SHUTDOWN_COMP + SCTP_CANT_STR_ASSOC +) + +var nativeEndian binary.ByteOrder +var sndRcvInfoSize uintptr + +func init() { + i := uint16(1) + if *(*byte)(unsafe.Pointer(&i)) == 0 { + nativeEndian = binary.BigEndian + } else { + nativeEndian = binary.LittleEndian + } + info := SndRcvInfo{} + sndRcvInfoSize = unsafe.Sizeof(info) +} + +func toBuf(v interface{}) []byte { + var buf bytes.Buffer + binary.Write(&buf, nativeEndian, v) + return buf.Bytes() +} + +func htons(h uint16) uint16 { + if nativeEndian == binary.LittleEndian { + return (h << 8 & 0xff00) | (h >> 8 & 0xff) + } + return h +} + +var ntohs = htons + +// setInitOpts sets options for an SCTP association initialization +// see https://tools.ietf.org/html/rfc4960#page-25 +func setInitOpts(fd int, options InitMsg) error { + optlen := unsafe.Sizeof(options) + _, _, err := setsockopt(fd, SCTP_INITMSG, uintptr(unsafe.Pointer(&options)), uintptr(optlen)) + return err +} + +func setNumOstreams(fd, num int) error { + return setInitOpts(fd, InitMsg{NumOstreams: uint16(num)}) +} + +type SCTPAddr struct { + IPAddrs []net.IPAddr + Port int +} + +func (a *SCTPAddr) ToRawSockAddrBuf() []byte { + p := htons(uint16(a.Port)) + if len(a.IPAddrs) == 0 { // if a.IPAddrs list is empty - fall back to IPv4 zero addr + s := syscall.RawSockaddrInet4{ + Family: syscall.AF_INET, + Port: p, + } + copy(s.Addr[:], net.IPv4zero) + return toBuf(s) + } + buf := []byte{} + for _, ip := range a.IPAddrs { + ipBytes := ip.IP + if len(ipBytes) == 0 { + ipBytes = net.IPv4zero + } + if ip4 := ipBytes.To4(); ip4 != nil { + s := syscall.RawSockaddrInet4{ + Family: syscall.AF_INET, + Port: p, + } + copy(s.Addr[:], ip4) + buf = append(buf, toBuf(s)...) + } else { + var scopeid uint32 + ifi, err := net.InterfaceByName(ip.Zone) + if err == nil { + scopeid = uint32(ifi.Index) + } + s := syscall.RawSockaddrInet6{ + Family: syscall.AF_INET6, + Port: p, + Scope_id: scopeid, + } + copy(s.Addr[:], ipBytes) + buf = append(buf, toBuf(s)...) + } + } + return buf +} + +func (a *SCTPAddr) String() string { + var b bytes.Buffer + + for n, i := range a.IPAddrs { + if i.IP.To4() != nil { + b.WriteString(i.String()) + } else if i.IP.To16() != nil { + b.WriteRune('[') + b.WriteString(i.String()) + b.WriteRune(']') + } + if n < len(a.IPAddrs)-1 { + b.WriteRune('/') + } + } + b.WriteRune(':') + b.WriteString(strconv.Itoa(a.Port)) + return b.String() +} + +func (a *SCTPAddr) Network() string { return "sctp" } + +func ResolveSCTPAddr(network, addrs string) (*SCTPAddr, error) { + tcpnet := "" + switch network { + case "", "sctp": + tcpnet = "tcp" + case "sctp4": + tcpnet = "tcp4" + case "sctp6": + tcpnet = "tcp6" + default: + return nil, fmt.Errorf("invalid net: %s", network) + } + elems := strings.Split(addrs, "/") + if len(elems) == 0 { + return nil, fmt.Errorf("invalid input: %s", addrs) + } + ipaddrs := make([]net.IPAddr, 0, len(elems)) + for _, e := range elems[:len(elems)-1] { + tcpa, err := net.ResolveTCPAddr(tcpnet, e+":") + if err != nil { + return nil, err + } + ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone}) + } + tcpa, err := net.ResolveTCPAddr(tcpnet, elems[len(elems)-1]) + if err != nil { + return nil, err + } + if tcpa.IP != nil { + ipaddrs = append(ipaddrs, net.IPAddr{IP: tcpa.IP, Zone: tcpa.Zone}) + } else { + ipaddrs = nil + } + return &SCTPAddr{ + IPAddrs: ipaddrs, + Port: tcpa.Port, + }, nil +} + +func SCTPConnect(fd int, addr *SCTPAddr) (int, error) { + buf := addr.ToRawSockAddrBuf() + param := GetAddrsOld{ + AddrNum: int32(len(buf)), + Addrs: uintptr(uintptr(unsafe.Pointer(&buf[0]))), + } + optlen := unsafe.Sizeof(param) + _, _, err := getsockopt(fd, SCTP_SOCKOPT_CONNECTX3, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) + if err == nil { + return int(param.AssocID), nil + } else if err != syscall.ENOPROTOOPT { + return 0, err + } + r0, _, err := setsockopt(fd, SCTP_SOCKOPT_CONNECTX, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) + return int(r0), err +} + +func SCTPBind(fd int, addr *SCTPAddr, flags int) error { + var option uintptr + switch flags { + case SCTP_BINDX_ADD_ADDR: + option = SCTP_SOCKOPT_BINDX_ADD + case SCTP_BINDX_REM_ADDR: + option = SCTP_SOCKOPT_BINDX_REM + default: + return syscall.EINVAL + } + + buf := addr.ToRawSockAddrBuf() + _, _, err := setsockopt(fd, option, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf))) + return err +} + +type SCTPConn struct { + _fd int32 + notificationHandler NotificationHandler +} + +func (c *SCTPConn) fd() int { + return int(atomic.LoadInt32(&c._fd)) +} + +func NewSCTPConn(fd int, handler NotificationHandler) *SCTPConn { + conn := &SCTPConn{ + _fd: int32(fd), + notificationHandler: handler, + } + return conn +} + +func (c *SCTPConn) Write(b []byte) (int, error) { + return c.SCTPWrite(b, nil) +} + +func (c *SCTPConn) Read(b []byte) (int, error) { + n, _, err := c.SCTPRead(b) + if n < 0 { + n = 0 + } + return n, err +} + +func (c *SCTPConn) SetInitMsg(numOstreams, maxInstreams, maxAttempts, maxInitTimeout int) error { + return setInitOpts(c.fd(), InitMsg{ + NumOstreams: uint16(numOstreams), + MaxInstreams: uint16(maxInstreams), + MaxAttempts: uint16(maxAttempts), + MaxInitTimeout: uint16(maxInitTimeout), + }) +} + +func (c *SCTPConn) SubscribeEvents(flags int) error { + var d, a, ad, sf, p, sh, pa, ada, au, se uint8 + if flags&SCTP_EVENT_DATA_IO > 0 { + d = 1 + } + if flags&SCTP_EVENT_ASSOCIATION > 0 { + a = 1 + } + if flags&SCTP_EVENT_ADDRESS > 0 { + ad = 1 + } + if flags&SCTP_EVENT_SEND_FAILURE > 0 { + sf = 1 + } + if flags&SCTP_EVENT_PEER_ERROR > 0 { + p = 1 + } + if flags&SCTP_EVENT_SHUTDOWN > 0 { + sh = 1 + } + if flags&SCTP_EVENT_PARTIAL_DELIVERY > 0 { + pa = 1 + } + if flags&SCTP_EVENT_ADAPTATION_LAYER > 0 { + ada = 1 + } + if flags&SCTP_EVENT_AUTHENTICATION > 0 { + au = 1 + } + if flags&SCTP_EVENT_SENDER_DRY > 0 { + se = 1 + } + param := EventSubscribe{ + DataIO: d, + Association: a, + Address: ad, + SendFailure: sf, + PeerError: p, + Shutdown: sh, + PartialDelivery: pa, + AdaptationLayer: ada, + Authentication: au, + SenderDry: se, + } + optlen := unsafe.Sizeof(param) + _, _, err := setsockopt(c.fd(), SCTP_EVENTS, uintptr(unsafe.Pointer(¶m)), uintptr(optlen)) + return err +} + +func (c *SCTPConn) SubscribedEvents() (int, error) { + param := EventSubscribe{} + optlen := unsafe.Sizeof(param) + _, _, err := getsockopt(c.fd(), SCTP_EVENTS, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) + if err != nil { + return 0, err + } + var flags int + if param.DataIO > 0 { + flags |= SCTP_EVENT_DATA_IO + } + if param.Association > 0 { + flags |= SCTP_EVENT_ASSOCIATION + } + if param.Address > 0 { + flags |= SCTP_EVENT_ADDRESS + } + if param.SendFailure > 0 { + flags |= SCTP_EVENT_SEND_FAILURE + } + if param.PeerError > 0 { + flags |= SCTP_EVENT_PEER_ERROR + } + if param.Shutdown > 0 { + flags |= SCTP_EVENT_SHUTDOWN + } + if param.PartialDelivery > 0 { + flags |= SCTP_EVENT_PARTIAL_DELIVERY + } + if param.AdaptationLayer > 0 { + flags |= SCTP_EVENT_ADAPTATION_LAYER + } + if param.Authentication > 0 { + flags |= SCTP_EVENT_AUTHENTICATION + } + if param.SenderDry > 0 { + flags |= SCTP_EVENT_SENDER_DRY + } + return flags, nil +} + +func (c *SCTPConn) SetDefaultSentParam(info *SndRcvInfo) error { + optlen := unsafe.Sizeof(*info) + _, _, err := setsockopt(c.fd(), SCTP_DEFAULT_SENT_PARAM, uintptr(unsafe.Pointer(info)), uintptr(optlen)) + return err +} + +func (c *SCTPConn) GetDefaultSentParam() (*SndRcvInfo, error) { + info := &SndRcvInfo{} + optlen := unsafe.Sizeof(*info) + _, _, err := getsockopt(c.fd(), SCTP_DEFAULT_SENT_PARAM, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(&optlen))) + return info, err +} + +func resolveFromRawAddr(ptr unsafe.Pointer, n int) (*SCTPAddr, error) { + addr := &SCTPAddr{ + IPAddrs: make([]net.IPAddr, n), + } + + switch family := (*(*syscall.RawSockaddrAny)(ptr)).Addr.Family; family { + case syscall.AF_INET: + addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port))) + tmp := syscall.RawSockaddrInet4{} + size := unsafe.Sizeof(tmp) + for i := 0; i < n; i++ { + a := *(*syscall.RawSockaddrInet4)(unsafe.Pointer( + uintptr(ptr) + size*uintptr(i))) + addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:]} + } + case syscall.AF_INET6: + addr.Port = int(ntohs(uint16((*(*syscall.RawSockaddrInet4)(ptr)).Port))) + tmp := syscall.RawSockaddrInet6{} + size := unsafe.Sizeof(tmp) + for i := 0; i < n; i++ { + a := *(*syscall.RawSockaddrInet6)(unsafe.Pointer( + uintptr(ptr) + size*uintptr(i))) + var zone string + ifi, err := net.InterfaceByIndex(int(a.Scope_id)) + if err == nil { + zone = ifi.Name + } + addr.IPAddrs[i] = net.IPAddr{IP: a.Addr[:], Zone: zone} + } + default: + return nil, fmt.Errorf("unknown address family: %d", family) + } + return addr, nil +} + +func sctpGetAddrs(fd, id, optname int) (*SCTPAddr, error) { + + type getaddrs struct { + assocId int32 + addrNum uint32 + addrs [4096]byte + } + param := getaddrs{ + assocId: int32(id), + } + optlen := unsafe.Sizeof(param) + _, _, err := getsockopt(fd, uintptr(optname), uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) + if err != nil { + return nil, err + } + return resolveFromRawAddr(unsafe.Pointer(¶m.addrs), int(param.addrNum)) +} + +func (c *SCTPConn) SCTPGetPrimaryPeerAddr() (*SCTPAddr, error) { + + type sctpGetSetPrim struct { + assocId int32 + addrs [128]byte + } + param := sctpGetSetPrim{ + assocId: int32(0), + } + optlen := unsafe.Sizeof(param) + _, _, err := getsockopt(c.fd(), SCTP_PRIMARY_ADDR, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) + if err != nil { + return nil, err + } + return resolveFromRawAddr(unsafe.Pointer(¶m.addrs), 1) +} + +func (c *SCTPConn) SCTPLocalAddr(id int) (*SCTPAddr, error) { + return sctpGetAddrs(c.fd(), id, SCTP_GET_LOCAL_ADDRS) +} + +func (c *SCTPConn) SCTPRemoteAddr(id int) (*SCTPAddr, error) { + return sctpGetAddrs(c.fd(), id, SCTP_GET_PEER_ADDRS) +} + +func (c *SCTPConn) LocalAddr() net.Addr { + addr, err := sctpGetAddrs(c.fd(), 0, SCTP_GET_LOCAL_ADDRS) + if err != nil { + return nil + } + return addr +} + +func (c *SCTPConn) RemoteAddr() net.Addr { + addr, err := sctpGetAddrs(c.fd(), 0, SCTP_GET_PEER_ADDRS) + if err != nil { + return nil + } + return addr +} + +func (c *SCTPConn) PeelOff(id int) (*SCTPConn, error) { + type peeloffArg struct { + assocId int32 + sd int + } + param := peeloffArg{ + assocId: int32(id), + } + optlen := unsafe.Sizeof(param) + _, _, err := getsockopt(c.fd(), SCTP_SOCKOPT_PEELOFF, uintptr(unsafe.Pointer(¶m)), uintptr(unsafe.Pointer(&optlen))) + if err != nil { + return nil, err + } + return &SCTPConn{_fd: int32(param.sd)}, nil +} + +func (c *SCTPConn) SetDeadline(t time.Time) error { + return syscall.EOPNOTSUPP +} + +func (c *SCTPConn) SetReadDeadline(t time.Time) error { + return syscall.EOPNOTSUPP +} + +func (c *SCTPConn) SetWriteDeadline(t time.Time) error { + return syscall.EOPNOTSUPP +} + +type SCTPListener struct { + fd int + m sync.Mutex +} + +func (ln *SCTPListener) Addr() net.Addr { + laddr, err := sctpGetAddrs(ln.fd, 0, SCTP_GET_LOCAL_ADDRS) + if err != nil { + return nil + } + return laddr +} + +type SCTPSndRcvInfoWrappedConn struct { + conn *SCTPConn +} + +func NewSCTPSndRcvInfoWrappedConn(conn *SCTPConn) *SCTPSndRcvInfoWrappedConn { + conn.SubscribeEvents(SCTP_EVENT_DATA_IO) + return &SCTPSndRcvInfoWrappedConn{conn} +} + +func (c *SCTPSndRcvInfoWrappedConn) Write(b []byte) (int, error) { + if len(b) < int(sndRcvInfoSize) { + return 0, syscall.EINVAL + } + info := (*SndRcvInfo)(unsafe.Pointer(&b[0])) + n, err := c.conn.SCTPWrite(b[sndRcvInfoSize:], info) + return n + int(sndRcvInfoSize), err +} + +func (c *SCTPSndRcvInfoWrappedConn) Read(b []byte) (int, error) { + if len(b) < int(sndRcvInfoSize) { + return 0, syscall.EINVAL + } + n, info, err := c.conn.SCTPRead(b[sndRcvInfoSize:]) + if err != nil { + return n, err + } + copy(b, toBuf(info)) + return n + int(sndRcvInfoSize), err +} + +func (c *SCTPSndRcvInfoWrappedConn) Close() error { + return c.conn.Close() +} + +func (c *SCTPSndRcvInfoWrappedConn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +func (c *SCTPSndRcvInfoWrappedConn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +func (c *SCTPSndRcvInfoWrappedConn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +func (c *SCTPSndRcvInfoWrappedConn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +func (c *SCTPSndRcvInfoWrappedConn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} + +func (c *SCTPSndRcvInfoWrappedConn) SetWriteBuffer(bytes int) error { + return c.conn.SetWriteBuffer(bytes) +} + +func (c *SCTPSndRcvInfoWrappedConn) GetWriteBuffer() (int, error) { + return c.conn.GetWriteBuffer() +} + +func (c *SCTPSndRcvInfoWrappedConn) SetReadBuffer(bytes int) error { + return c.conn.SetReadBuffer(bytes) +} + +func (c *SCTPSndRcvInfoWrappedConn) GetReadBuffer() (int, error) { + return c.conn.GetReadBuffer() +} + +// SocketConfig contains options for the SCTP socket. +type SocketConfig struct { + // If Control is not nil it is called after the socket is created but before + // it is bound or connected. + Control func(network, address string, c syscall.RawConn) error + + // InitMsg is the options to send in the initial SCTP message + InitMsg InitMsg +} + +func (cfg *SocketConfig) Listen(net string, laddr *SCTPAddr) (*SCTPListener, error) { + return listenSCTPExtConfig(net, laddr, cfg.InitMsg, cfg.Control) +} + +func (cfg *SocketConfig) Dial(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { + return dialSCTPExtConfig(net, laddr, raddr, cfg.InitMsg, cfg.Control) +} diff --git a/vendor/github.com/ishidawataru/sctp/sctp_linux.go b/vendor/github.com/ishidawataru/sctp/sctp_linux.go new file mode 100644 index 00000000000..ac340ddfbfa --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/sctp_linux.go @@ -0,0 +1,305 @@ +// +build linux,!386 +// Copyright 2019 Wataru Ishida. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sctp + +import ( + "io" + "net" + "sync/atomic" + "syscall" + "unsafe" +) + +func setsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) { + // FIXME: syscall.SYS_SETSOCKOPT is undefined on 386 + r0, r1, errno := syscall.Syscall6(syscall.SYS_SETSOCKOPT, + uintptr(fd), + SOL_SCTP, + optname, + optval, + optlen, + 0) + if errno != 0 { + return r0, r1, errno + } + return r0, r1, nil +} + +func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) { + // FIXME: syscall.SYS_GETSOCKOPT is undefined on 386 + r0, r1, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, + uintptr(fd), + SOL_SCTP, + optname, + optval, + optlen, + 0) + if errno != 0 { + return r0, r1, errno + } + return r0, r1, nil +} + +type rawConn struct { + sockfd int +} + +func (r rawConn) Control(f func(fd uintptr)) error { + f(uintptr(r.sockfd)) + return nil +} + +func (r rawConn) Read(f func(fd uintptr) (done bool)) error { + panic("not implemented") +} + +func (r rawConn) Write(f func(fd uintptr) (done bool)) error { + panic("not implemented") +} + +func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) { + var cbuf []byte + if info != nil { + cmsgBuf := toBuf(info) + hdr := &syscall.Cmsghdr{ + Level: syscall.IPPROTO_SCTP, + Type: SCTP_CMSG_SNDRCV, + } + + // bitwidth of hdr.Len is platform-specific, + // so we use hdr.SetLen() rather than directly setting hdr.Len + hdr.SetLen(syscall.CmsgSpace(len(cmsgBuf))) + cbuf = append(toBuf(hdr), cmsgBuf...) + } + return syscall.SendmsgN(c.fd(), b, cbuf, nil, 0) +} + +func parseSndRcvInfo(b []byte) (*SndRcvInfo, error) { + msgs, err := syscall.ParseSocketControlMessage(b) + if err != nil { + return nil, err + } + for _, m := range msgs { + if m.Header.Level == syscall.IPPROTO_SCTP { + switch m.Header.Type { + case SCTP_CMSG_SNDRCV: + return (*SndRcvInfo)(unsafe.Pointer(&m.Data[0])), nil + } + } + } + return nil, nil +} + +func (c *SCTPConn) SCTPRead(b []byte) (int, *SndRcvInfo, error) { + oob := make([]byte, 254) + for { + n, oobn, recvflags, _, err := syscall.Recvmsg(c.fd(), b, oob, 0) + if err != nil { + return n, nil, err + } + + if n == 0 && oobn == 0 { + return 0, nil, io.EOF + } + + if recvflags&MSG_NOTIFICATION > 0 && c.notificationHandler != nil { + if err := c.notificationHandler(b[:n]); err != nil { + return 0, nil, err + } + } else { + var info *SndRcvInfo + if oobn > 0 { + info, err = parseSndRcvInfo(oob[:oobn]) + } + return n, info, err + } + } +} + +func (c *SCTPConn) Close() error { + if c != nil { + fd := atomic.SwapInt32(&c._fd, -1) + if fd > 0 { + info := &SndRcvInfo{ + Flags: SCTP_EOF, + } + c.SCTPWrite(nil, info) + syscall.Shutdown(int(fd), syscall.SHUT_RDWR) + return syscall.Close(int(fd)) + } + } + return syscall.EBADF +} + +func (c *SCTPConn) SetWriteBuffer(bytes int) error { + return syscall.SetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes) +} + +func (c *SCTPConn) GetWriteBuffer() (int, error) { + return syscall.GetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_SNDBUF) +} + +func (c *SCTPConn) SetReadBuffer(bytes int) error { + return syscall.SetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes) +} + +func (c *SCTPConn) GetReadBuffer() (int, error) { + return syscall.GetsockoptInt(c.fd(), syscall.SOL_SOCKET, syscall.SO_RCVBUF) +} + +// ListenSCTP - start listener on specified address/port +func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { + return ListenSCTPExt(net, laddr, InitMsg{NumOstreams: SCTP_MAX_STREAM}) +} + +// ListenSCTPExt - start listener on specified address/port with given SCTP options +func ListenSCTPExt(network string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) { + return listenSCTPExtConfig(network, laddr, options, nil) +} + +// listenSCTPExtConfig - start listener on specified address/port with given SCTP options and socket configuration +func listenSCTPExtConfig(network string, laddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPListener, error) { + af, ipv6only := favoriteAddrFamily(network, laddr, nil, "listen") + sock, err := syscall.Socket( + af, + syscall.SOCK_STREAM, + syscall.IPPROTO_SCTP, + ) + if err != nil { + return nil, err + } + + // close socket on error + defer func() { + if err != nil { + syscall.Close(sock) + } + }() + if err = setDefaultSockopts(sock, af, ipv6only); err != nil { + return nil, err + } + if control != nil { + rc := rawConn{sockfd: sock} + if err = control(network, laddr.String(), rc); err != nil { + return nil, err + } + } + err = setInitOpts(sock, options) + if err != nil { + return nil, err + } + + if laddr != nil { + // If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address + if len(laddr.IPAddrs) == 0 { + if af == syscall.AF_INET { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero}) + } else if af == syscall.AF_INET6 { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero}) + } + } + err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) + if err != nil { + return nil, err + } + } + err = syscall.Listen(sock, syscall.SOMAXCONN) + if err != nil { + return nil, err + } + return &SCTPListener{ + fd: sock, + }, nil +} + +// AcceptSCTP waits for and returns the next SCTP connection to the listener. +func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) { + fd, _, err := syscall.Accept4(ln.fd, 0) + return NewSCTPConn(fd, nil), err +} + +// Accept waits for and returns the next connection connection to the listener. +func (ln *SCTPListener) Accept() (net.Conn, error) { + return ln.AcceptSCTP() +} + +func (ln *SCTPListener) Close() error { + syscall.Shutdown(ln.fd, syscall.SHUT_RDWR) + return syscall.Close(ln.fd) +} + +// DialSCTP - bind socket to laddr (if given) and connect to raddr +func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { + return DialSCTPExt(net, laddr, raddr, InitMsg{NumOstreams: SCTP_MAX_STREAM}) +} + +// DialSCTPExt - same as DialSCTP but with given SCTP options +func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) { + return dialSCTPExtConfig(network, laddr, raddr, options, nil) +} + +// dialSCTPExtConfig - same as DialSCTP but with given SCTP options and socket configuration +func dialSCTPExtConfig(network string, laddr, raddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPConn, error) { + af, ipv6only := favoriteAddrFamily(network, laddr, raddr, "dial") + sock, err := syscall.Socket( + af, + syscall.SOCK_STREAM, + syscall.IPPROTO_SCTP, + ) + if err != nil { + return nil, err + } + + // close socket on error + defer func() { + if err != nil { + syscall.Close(sock) + } + }() + if err = setDefaultSockopts(sock, af, ipv6only); err != nil { + return nil, err + } + if control != nil { + rc := rawConn{sockfd: sock} + if err = control(network, laddr.String(), rc); err != nil { + return nil, err + } + } + err = setInitOpts(sock, options) + if err != nil { + return nil, err + } + if laddr != nil { + // If IP address and/or port was not provided so far, let's use the unspecified IPv4 or IPv6 address + if len(laddr.IPAddrs) == 0 { + if af == syscall.AF_INET { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv4zero}) + } else if af == syscall.AF_INET6 { + laddr.IPAddrs = append(laddr.IPAddrs, net.IPAddr{IP: net.IPv6zero}) + } + } + err := SCTPBind(sock, laddr, SCTP_BINDX_ADD_ADDR) + if err != nil { + return nil, err + } + } + _, err = SCTPConnect(sock, raddr) + if err != nil { + return nil, err + } + return NewSCTPConn(sock, nil), nil +} diff --git a/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go b/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go new file mode 100644 index 00000000000..118fe159e92 --- /dev/null +++ b/vendor/github.com/ishidawataru/sctp/sctp_unsupported.go @@ -0,0 +1,98 @@ +// +build !linux linux,386 +// Copyright 2019 Wataru Ishida. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sctp + +import ( + "errors" + "net" + "runtime" + "syscall" +) + +var ErrUnsupported = errors.New("SCTP is unsupported on " + runtime.GOOS + "/" + runtime.GOARCH) + +func setsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) { + return 0, 0, ErrUnsupported +} + +func getsockopt(fd int, optname, optval, optlen uintptr) (uintptr, uintptr, error) { + return 0, 0, ErrUnsupported +} + +func (c *SCTPConn) SCTPWrite(b []byte, info *SndRcvInfo) (int, error) { + return 0, ErrUnsupported +} + +func (c *SCTPConn) SCTPRead(b []byte) (int, *SndRcvInfo, error) { + return 0, nil, ErrUnsupported +} + +func (c *SCTPConn) Close() error { + return ErrUnsupported +} + +func (c *SCTPConn) SetWriteBuffer(bytes int) error { + return ErrUnsupported +} + +func (c *SCTPConn) GetWriteBuffer() (int, error) { + return 0, ErrUnsupported +} + +func (c *SCTPConn) SetReadBuffer(bytes int) error { + return ErrUnsupported +} + +func (c *SCTPConn) GetReadBuffer() (int, error) { + return 0, ErrUnsupported +} + +func ListenSCTP(net string, laddr *SCTPAddr) (*SCTPListener, error) { + return nil, ErrUnsupported +} + +func ListenSCTPExt(net string, laddr *SCTPAddr, options InitMsg) (*SCTPListener, error) { + return nil, ErrUnsupported +} + +func listenSCTPExtConfig(network string, laddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPListener, error) { + return nil, ErrUnsupported +} + +func (ln *SCTPListener) Accept() (net.Conn, error) { + return nil, ErrUnsupported +} + +func (ln *SCTPListener) AcceptSCTP() (*SCTPConn, error) { + return nil, ErrUnsupported +} + +func (ln *SCTPListener) Close() error { + return ErrUnsupported +} + +func DialSCTP(net string, laddr, raddr *SCTPAddr) (*SCTPConn, error) { + return nil, ErrUnsupported +} + +func DialSCTPExt(network string, laddr, raddr *SCTPAddr, options InitMsg) (*SCTPConn, error) { + return nil, ErrUnsupported +} + +func dialSCTPExtConfig(network string, laddr, raddr *SCTPAddr, options InitMsg, control func(network, address string, c syscall.RawConn) error) (*SCTPConn, error) { + return nil, ErrUnsupported +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 3de1da5b3a2..cdd1bc8d6c0 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -160,6 +160,9 @@ {"path":"github.com/docker/go-metrics","checksumSHA1":"kHVt4M5Pfby2dhurp+hZJfQhzVU=","revision":"399ea8c73916000c64c2c76e8da00ca82f8387ab","revisionTime":"2018-02-09T01:25:29Z"}, {"path":"github.com/docker/go-units","checksumSHA1":"18hmvak2Dc9x5cgKeZ2iApviT7w=","comment":"v0.1.0-23-g5d2041e","revision":"5d2041e26a699eaca682e2ea41c8f891e1060444"}, {"path":"github.com/docker/libnetwork/ipamutils","checksumSHA1":"X07lwsZTwq6wVkKDAPxyTmimwq8=","origin":"github.com/moby/libnetwork/ipamutils","revision":"ef149a924dfde2e506ea3cb3f617d7d0fa96b8ee","revisionTime":"2020-03-18T18:26:00Z"}, + {"path":"github.com/docker/libnetwork/resolvconf","checksumSHA1":"lY3zVxmWyfZOanY03iGiq0hLuvI=","revision":"ef149a924dfde2e506ea3cb3f617d7d0fa96b8ee","revisionTime":"2020-03-18T18:26:00Z"}, + {"path":"github.com/docker/libnetwork/resolvconf/dns","checksumSHA1":"trPIMJU2ZUE4myk/9hZw1GYkqQo=","revision":"ef149a924dfde2e506ea3cb3f617d7d0fa96b8ee","revisionTime":"2020-03-18T18:26:00Z"}, + {"path":"github.com/docker/libnetwork/types","checksumSHA1":"emCh8kUCHRn7Me4v1oNdekVh+Xo=","revision":"1a17fb36132631a95fe6bb055b91e24a516ad81d","revisionTime":"2020-04-09T22:01:08Z"}, {"path":"github.com/dustin/go-humanize","checksumSHA1":"xteP9Px90oMrg/HZuqvZkpXCR+s=","revision":"8929fe90cee4b2cb9deb468b51fb34eba64d1bf0"}, {"path":"github.com/elazarl/go-bindata-assetfs","checksumSHA1":"7DxViusFRJ7UPH0jZqYatwDrOkY=","revision":"30f82fa23fd844bd5bb1e5f216db87fd77b5eb43","revisionTime":"2017-02-27T21:27:28Z"}, {"path":"github.com/fatih/color","checksumSHA1":"VsE3zx2d8kpwj97TWhYddzAwBrY=","revision":"507f6050b8568533fb3f5504de8e5205fa62a114","revisionTime":"2018-02-13T13:34:03Z"}, @@ -287,6 +290,7 @@ {"path":"github.com/hashicorp/yamux","checksumSHA1":"ldkAQ1CpiAaQ9sti0qIch+UyRsI=","revision":"7221087c3d281fda5f794e28c2ea4c6e4d5c4558","revisionTime":"2018-09-17T20:50:41Z"}, {"path":"github.com/hpcloud/tail/util","checksumSHA1":"0xM336Lb25URO/1W1/CtGoRygVU=","revision":"37f4271387456dd1bf82ab1ad9229f060cc45386","revisionTime":"2017-08-14T16:06:53Z"}, {"path":"github.com/hpcloud/tail/watch","checksumSHA1":"TP4OAv5JMtzj2TB6OQBKqauaKDc=","revision":"37f4271387456dd1bf82ab1ad9229f060cc45386","revisionTime":"2017-08-14T16:06:53Z"}, + {"path":"github.com/ishidawataru/sctp","checksumSHA1":"HtKWN1ivg+EgHK+uPfiPXz4r1+s=","revision":"00ab2ac2db07a138417639ef3f39672c65dbb9a0","revisionTime":"2019-12-11T19:11:52Z"}, {"path":"github.com/jmespath/go-jmespath","checksumSHA1":"3/Bhy+ua/DCv2ElMD5GzOYSGN6g=","comment":"0.2.2-2-gc01cf91","revision":"c01cf91b011868172fdcd9f41838e80c9d716264"}, {"path":"github.com/konsorten/go-windows-terminal-sequences","checksumSHA1":"Aulh7C5SVOA4Jzt5eHNH6197Mbk=","revision":"f55edac94c9bbba5d6182a4be46d86a2c9b5b50e","revisionTime":"2019-02-26T22:47:05Z"}, {"path":"github.com/kr/pretty","checksumSHA1":"eOXF2PEvYLMeD8DSzLZJWbjYzco=","revision":"cfb55aafdaf3ec08f0db22699ab822c50091b1c4","revisionTime":"2016-08-23T17:07:15Z"}, From 4400639d0f36394946cf466534e316a7167d2e22 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Mon, 20 Apr 2020 14:08:50 -0400 Subject: [PATCH 09/18] resolvconf: use libcontainer/resolvconf for parsing/building --- drivers/shared/resolvconf/mount.go | 13 +--- drivers/shared/resolvconf/resolvconf.go | 44 -------------- drivers/shared/resolvconf/resolvconf_test.go | 52 ---------------- plugins/drivers/testutils/dns_testing.go | 63 ++++---------------- 4 files changed, 12 insertions(+), 160 deletions(-) delete mode 100644 drivers/shared/resolvconf/resolvconf.go delete mode 100644 drivers/shared/resolvconf/resolvconf_test.go diff --git a/drivers/shared/resolvconf/mount.go b/drivers/shared/resolvconf/mount.go index a102d2e27ed..386c8099eaa 100644 --- a/drivers/shared/resolvconf/mount.go +++ b/drivers/shared/resolvconf/mount.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + dresolvconf "github.com/docker/libnetwork/resolvconf" "github.com/hashicorp/nomad/plugins/drivers" ) @@ -32,20 +33,10 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo return mount, nil } - f, err := os.Create(path) + _, err := dresolvconf.Build(path, conf.Servers, conf.Searches, conf.Options) if err != nil { return nil, err } - defer f.Close() - - rc, err := New(conf.Servers, conf.Searches, conf.Options) - if err != nil { - return nil, err - } - - if _, err := f.Write(rc.Content()); err != nil { - return nil, err - } return mount, nil } diff --git a/drivers/shared/resolvconf/resolvconf.go b/drivers/shared/resolvconf/resolvconf.go deleted file mode 100644 index 7e6ba23abf3..00000000000 --- a/drivers/shared/resolvconf/resolvconf.go +++ /dev/null @@ -1,44 +0,0 @@ -package resolvconf - -import ( - "bytes" - "io/ioutil" - "strings" -) - -type ResolvConf struct { - servers []string - searches []string - options []string -} - -func New(servers []string, searches []string, options []string) (*ResolvConf, error) { - return &ResolvConf{ - servers: servers, - searches: searches, - options: options, - }, nil -} - -func (rc *ResolvConf) Content() []byte { - content := bytes.NewBuffer(nil) - if len(rc.searches) > 0 { - if searchString := strings.Join(rc.searches, " "); strings.Trim(searchString, " ") != "." { - content.WriteString("search " + searchString + "\n") - } - } - for _, dns := range rc.servers { - content.WriteString("nameserver " + dns + "\n") - } - if len(rc.options) > 0 { - if optsString := strings.Join(rc.options, " "); strings.Trim(optsString, " ") != "" { - content.WriteString("options " + optsString + "\n") - } - } - - return content.Bytes() -} - -func (rc *ResolvConf) WriteToPath(path string) error { - return ioutil.WriteFile(path, rc.Content(), 0644) -} diff --git a/drivers/shared/resolvconf/resolvconf_test.go b/drivers/shared/resolvconf/resolvconf_test.go deleted file mode 100644 index 24f05fea197..00000000000 --- a/drivers/shared/resolvconf/resolvconf_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package resolvconf - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestResolvConf_Content(t *testing.T) { - cases := []struct { - name string - in *ResolvConf - out []byte - }{ - { - name: "empty", - in: &ResolvConf{}, - out: nil, - }, - { - name: "severs", - in: &ResolvConf{servers: []string{"8.8.8.8", "8.8.4.4"}}, - out: []byte("nameserver 8.8.8.8\nnameserver 8.8.4.4\n"), - }, - { - name: "ipv6 servers", - in: &ResolvConf{servers: []string{"2606:4700:4700::1111", "2606:4700:4700::1001"}}, - out: []byte("nameserver 2606:4700:4700::1111\nnameserver 2606:4700:4700::1001\n"), - }, - { - name: "search servers", - in: &ResolvConf{servers: []string{"1.1.1.1", "1.0.0.1"}, searches: []string{"infra.nomad", "local.test"}}, - out: []byte("search infra.nomad local.test\nnameserver 1.1.1.1\nnameserver 1.0.0.1\n"), - }, - { - name: "full example", - in: &ResolvConf{ - servers: []string{"1.1.1.1", "1.0.0.1"}, - searches: []string{"infra.nomad", "local.test"}, - options: []string{"ndots:2", "edns0"}, - }, - out: []byte("search infra.nomad local.test\nnameserver 1.1.1.1\nnameserver 1.0.0.1\noptions ndots:2 edns0\n"), - }, - } - - for _, c := range cases { - t.Run(c.name, func(childT *testing.T) { - childT.Parallel() - require.Equal(childT, c.out, c.in.Content()) - }) - } -} diff --git a/plugins/drivers/testutils/dns_testing.go b/plugins/drivers/testutils/dns_testing.go index 318e8e750cf..00d8d4f2394 100644 --- a/plugins/drivers/testutils/dns_testing.go +++ b/plugins/drivers/testutils/dns_testing.go @@ -1,10 +1,11 @@ package testutils import ( - "io/ioutil" "strings" "testing" + dresolvconf "github.com/docker/libnetwork/resolvconf" + dtypes "github.com/docker/libnetwork/types" "github.com/hashicorp/nomad/plugins/drivers" "github.com/stretchr/testify/require" ) @@ -25,62 +26,18 @@ func TestTaskDNSConfig(t *testing.T, driver *DriverHarness, taskID string, dns * false, "") require.Zero(t, r.exitCode) - resolvConf := strings.TrimSpace(r.stdout) + resolvConf := []byte(strings.TrimSpace(r.stdout)) if dns != nil { - require.ElementsMatch(t, dns.Servers, getNameservers(resolvConf)) - require.ElementsMatch(t, dns.Searches, getSearchDomains(resolvConf)) - require.ElementsMatch(t, dns.Options, getOptions(resolvConf)) + require.ElementsMatch(t, dns.Servers, dresolvconf.GetNameservers(resolvConf, dtypes.IP)) + require.ElementsMatch(t, dns.Searches, dresolvconf.GetSearchDomains(resolvConf)) + require.ElementsMatch(t, dns.Options, dresolvconf.GetOptions(resolvConf)) } else { - system, err := ioutil.ReadFile("/etc/resolv.conf") + system, err := dresolvconf.Get() require.NoError(t, err) - require.ElementsMatch(t, getNameservers(string(system)), getNameservers(resolvConf)) - require.ElementsMatch(t, getSearchDomains(string(system)), getSearchDomains(resolvConf)) - require.ElementsMatch(t, getOptions(string(system)), getOptions(resolvConf)) + require.ElementsMatch(t, dresolvconf.GetNameservers(system.Content, dtypes.IP), dresolvconf.GetNameservers(resolvConf, dtypes.IP)) + require.ElementsMatch(t, dresolvconf.GetSearchDomains(system.Content), dresolvconf.GetSearchDomains(resolvConf)) + require.ElementsMatch(t, dresolvconf.GetOptions(system.Content), dresolvconf.GetOptions(resolvConf)) } }) } - -// getLines parses input into lines and strips away # comments. -func getLines(input string) []string { - lines := strings.Split(input, "\n") - var output []string - for _, currentLine := range lines { - var commentIndex = strings.Index(currentLine, "#") - if commentIndex == -1 { - output = append(output, currentLine) - } else { - output = append(output, currentLine[:commentIndex]) - } - } - return output -} -func getNameservers(resolvConf string) []string { - nameservers := []string{} - for _, line := range getLines(resolvConf) { - if strings.HasPrefix(line, "nameserver ") { - nameservers = append(nameservers, line[11:]) - } - } - return nameservers -} - -func getSearchDomains(resolvConf string) []string { - domains := []string{} - for _, line := range getLines(resolvConf) { - if strings.HasPrefix(line, "search ") { - domains = strings.Fields(line[7:]) - } - } - return domains -} - -func getOptions(resolvConf string) []string { - options := []string{} - for _, line := range getLines(resolvConf) { - if strings.HasPrefix(line, "options ") { - options = strings.Fields(line[8:]) - } - } - return options -} From 1e22d56bfc7248a132220baa68bf277f2406b48e Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Mon, 20 Apr 2020 15:05:26 -0400 Subject: [PATCH 10/18] resolvconf: use system resolvconf params if not overriden --- drivers/shared/resolvconf/mount.go | 24 +++++++++++++++++++++++- plugins/drivers/testutils/dns_testing.go | 12 +++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/drivers/shared/resolvconf/mount.go b/drivers/shared/resolvconf/mount.go index 386c8099eaa..226592c4bcd 100644 --- a/drivers/shared/resolvconf/mount.go +++ b/drivers/shared/resolvconf/mount.go @@ -6,6 +6,7 @@ import ( "path/filepath" dresolvconf "github.com/docker/libnetwork/resolvconf" + "github.com/docker/libnetwork/types" "github.com/hashicorp/nomad/plugins/drivers" ) @@ -33,6 +34,27 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo return mount, nil } + currRC, err := dresolvconf.Get() + if err != nil { + return nil, err + } + + var ( + err error + dnsList = dresolvconf.GetNameservers(currRC.Content, types.IP) + dnsSearchList = dresolvconf.GetSearchDomains(currRC.Content) + dnsOptionsList = dresolvconf.GetOptions(currRC.Content) + ) + if nServers > 0 { + dnsList = conf.Servers + } + if nSearches > 0 { + dnsSearchList = conf.Searches + } + if nOptions > 0 { + dnsOptionsList = conf.Options + } + _, err := dresolvconf.Build(path, conf.Servers, conf.Searches, conf.Options) if err != nil { return nil, err @@ -42,7 +64,7 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo } func copySystemDNS(dest string) error { - in, err := os.Open("/etc/resolv.conf") + in, err := os.Open(dresolvconf.Path()) if err != nil { return err } diff --git a/plugins/drivers/testutils/dns_testing.go b/plugins/drivers/testutils/dns_testing.go index 00d8d4f2394..f27a8fead6c 100644 --- a/plugins/drivers/testutils/dns_testing.go +++ b/plugins/drivers/testutils/dns_testing.go @@ -29,9 +29,15 @@ func TestTaskDNSConfig(t *testing.T, driver *DriverHarness, taskID string, dns * resolvConf := []byte(strings.TrimSpace(r.stdout)) if dns != nil { - require.ElementsMatch(t, dns.Servers, dresolvconf.GetNameservers(resolvConf, dtypes.IP)) - require.ElementsMatch(t, dns.Searches, dresolvconf.GetSearchDomains(resolvConf)) - require.ElementsMatch(t, dns.Options, dresolvconf.GetOptions(resolvConf)) + if len(dns.Servers) > 0 { + require.ElementsMatch(t, dns.Servers, dresolvconf.GetNameservers(resolvConf, dtypes.IP)) + } + if len(dns.Searches) > 0 { + require.ElementsMatch(t, dns.Searches, dresolvconf.GetSearchDomains(resolvConf)) + } + if len(dns.Options) > 0 { + require.ElementsMatch(t, dns.Options, dresolvconf.GetOptions(resolvConf)) + } } else { system, err := dresolvconf.Get() require.NoError(t, err) From 1c9ee92815432ccab1c57c4395f9a7cc72ba2ae9 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Mon, 20 Apr 2020 22:04:02 -0400 Subject: [PATCH 11/18] resolvconf: fix build errors --- drivers/shared/resolvconf/mount.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/shared/resolvconf/mount.go b/drivers/shared/resolvconf/mount.go index 226592c4bcd..b154d67812b 100644 --- a/drivers/shared/resolvconf/mount.go +++ b/drivers/shared/resolvconf/mount.go @@ -40,7 +40,6 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo } var ( - err error dnsList = dresolvconf.GetNameservers(currRC.Content, types.IP) dnsSearchList = dresolvconf.GetSearchDomains(currRC.Content) dnsOptionsList = dresolvconf.GetOptions(currRC.Content) @@ -55,7 +54,7 @@ func GenerateDNSMount(taskDir string, conf *drivers.DNSConfig) (*drivers.MountCo dnsOptionsList = conf.Options } - _, err := dresolvconf.Build(path, conf.Servers, conf.Searches, conf.Options) + _, err = dresolvconf.Build(path, dnsList, dnsSearchList, dnsOptionsList) if err != nil { return nil, err } From 63cc70b29e30dc4f037d2541640b389538928803 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Tue, 21 Apr 2020 23:15:25 -0400 Subject: [PATCH 12/18] structs: add diff context to DNSConfig --- nomad/structs/diff.go | 46 ++++++++++++++++++++++++++++++++++++++ nomad/structs/diff_test.go | 15 +++++++++++++ 2 files changed, 61 insertions(+) diff --git a/nomad/structs/diff.go b/nomad/structs/diff.go index 5e6eaf30654..0ff09a183a3 100644 --- a/nomad/structs/diff.go +++ b/nomad/structs/diff.go @@ -1055,6 +1055,52 @@ func (r *NetworkResource) Diff(other *NetworkResource, contextual bool) *ObjectD diff.Objects = append(diff.Objects, dynPorts...) } + if dnsDiff := r.DNS.Diff(other.DNS, contextual); dnsDiff != nil { + diff.Objects = append(diff.Objects, dnsDiff) + } + + return diff +} + +// Diff returns a diff of two DNSConfig structs +func (c *DNSConfig) Diff(other *DNSConfig, contextual bool) *ObjectDiff { + if reflect.DeepEqual(c, other) { + return nil + } + + flatten := func(conf *DNSConfig) map[string]string { + m := map[string]string{} + if len(conf.Servers) > 0 { + m["Servers"] = strings.Join(conf.Servers, ",") + } + if len(conf.Searches) > 0 { + m["Searches"] = strings.Join(conf.Searches, ",") + } + if len(conf.Options) > 0 { + m["Options"] = strings.Join(conf.Options, ",") + } + return m + } + + diff := &ObjectDiff{Type: DiffTypeNone, Name: "DNS"} + var oldPrimitiveFlat, newPrimitiveFlat map[string]string + if c == nil { + c = &DNSConfig{} + diff.Type = DiffTypeAdded + newPrimitiveFlat = flatten(other) + } else if other == nil { + other = &DNSConfig{} + diff.Type = DiffTypeDeleted + oldPrimitiveFlat = flatten(c) + } else { + diff.Type = DiffTypeEdited + oldPrimitiveFlat = flatten(c) + newPrimitiveFlat = flatten(other) + } + + // Diff the primitive fields. + diff.Fields = fieldDiffs(oldPrimitiveFlat, newPrimitiveFlat, contextual) + return diff } diff --git a/nomad/structs/diff_test.go b/nomad/structs/diff_test.go index c51f38d0f80..abccd21aa0d 100644 --- a/nomad/structs/diff_test.go +++ b/nomad/structs/diff_test.go @@ -2822,6 +2822,9 @@ func TestTaskGroupDiff(t *testing.T) { To: 8081, }, }, + DNS: &DNSConfig{ + Servers: []string{"1.1.1.1"}, + }, }, }, }, @@ -2864,6 +2867,18 @@ func TestTaskGroupDiff(t *testing.T) { }, }, }, + { + Type: DiffTypeAdded, + Name: "DNS", + Fields: []*FieldDiff{ + { + Type: DiffTypeAdded, + Name: "Servers", + Old: "", + New: "1.1.1.1", + }, + }, + }, }, }, { From 132a1345e336f93b8e207feb48915b91234e5a13 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Tue, 21 Apr 2020 23:31:23 -0400 Subject: [PATCH 13/18] website: add DNS options to network stanza jobspec docs --- .../pages/docs/job-specification/network.mdx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/website/pages/docs/job-specification/network.mdx b/website/pages/docs/job-specification/network.mdx index 4f267b116a2..e976a70c7b8 100644 --- a/website/pages/docs/job-specification/network.mdx +++ b/website/pages/docs/job-specification/network.mdx @@ -74,6 +74,10 @@ job "docs" { - `host` - Each task will join the host network namespace and a shared network namespace is not created. This matches the current behavior in Nomad 0.9. + - `dns` ([DNSConfig](#dns-parameters): nil) - Sets the DNS configuration + for the allocations. By default all DNS configuration is inherited from the client host. + DNS configuration is only supported on Linux clients at this time. + ### `port` Parameters - `static` `(int: nil)` - Specifies the static TCP/UDP port to allocate. If omitted, a dynamic port is chosen. We **do not recommend** using static ports, except @@ -98,6 +102,12 @@ When the task starts, it will be passed the following environment variables: The label of the port is just text - it has no special meaning to Nomad. +## `dns` Parameters + +- `servers` `(array: nil)` - Sets the dns nameservers the allocation uses for name resolution. +- `searches` `(array: nil)` - Sets the search list for hostname lookup +- `options` `(array: nil)` - Sets internal resolver variables. + ## `network` Examples The following examples only show the `network` stanzas. Remember that the @@ -202,6 +212,18 @@ network { } ``` +### DNS + +The following example configures the allocation to use Google's DNS resolvers 8.8.8.8 and 8.8.4.4. + +```hcl +network { + dns { + servers = ["8.8.8.8", "8.8.4.4"] + } +} +``` + ### Limitations - Only one `network` stanza can be specified, when it is defined at the task group level. From 920446f9d330a1cd59223b96005ef84dc45aa553 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Tue, 21 Apr 2020 23:35:53 -0400 Subject: [PATCH 14/18] command: add dns stanza comments to example jobspec --- command/assets/connect.nomad | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/command/assets/connect.nomad b/command/assets/connect.nomad index 59b6ca64354..c7f6c6404eb 100644 --- a/command/assets/connect.nomad +++ b/command/assets/connect.nomad @@ -256,6 +256,12 @@ job "countdash" { # port "http" { # to = "8080" # } + + # The "dns" stanza allows operators to override the DNS configuration + # inherited by the host client. + # dns { + # servers = ["1.1.1.1"] + # } } # The "service" stanza enables Consul Connect. service { From da45969905a1e9a7e25122820c74776225c5b1cf Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Wed, 22 Apr 2020 00:06:44 -0400 Subject: [PATCH 15/18] structs: lint --- nomad/structs/diff.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/nomad/structs/diff.go b/nomad/structs/diff.go index 0ff09a183a3..1e57eea2ddf 100644 --- a/nomad/structs/diff.go +++ b/nomad/structs/diff.go @@ -1085,11 +1085,9 @@ func (c *DNSConfig) Diff(other *DNSConfig, contextual bool) *ObjectDiff { diff := &ObjectDiff{Type: DiffTypeNone, Name: "DNS"} var oldPrimitiveFlat, newPrimitiveFlat map[string]string if c == nil { - c = &DNSConfig{} diff.Type = DiffTypeAdded newPrimitiveFlat = flatten(other) } else if other == nil { - other = &DNSConfig{} diff.Type = DiffTypeDeleted oldPrimitiveFlat = flatten(c) } else { From 5afa6bb9e1e5633d61a7a177ee747904c2208769 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Wed, 22 Apr 2020 22:32:10 -0400 Subject: [PATCH 16/18] Update website/pages/docs/job-specification/network.mdx Co-Authored-By: Tim Gross --- website/pages/docs/job-specification/network.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/pages/docs/job-specification/network.mdx b/website/pages/docs/job-specification/network.mdx index e976a70c7b8..d362597d023 100644 --- a/website/pages/docs/job-specification/network.mdx +++ b/website/pages/docs/job-specification/network.mdx @@ -104,7 +104,7 @@ The label of the port is just text - it has no special meaning to Nomad. ## `dns` Parameters -- `servers` `(array: nil)` - Sets the dns nameservers the allocation uses for name resolution. +- `servers` `(array: nil)` - Sets the DNS nameservers the allocation uses for name resolution. - `searches` `(array: nil)` - Sets the search list for hostname lookup - `options` `(array: nil)` - Sets internal resolver variables. From 1632a63e1295f58313a5c8878a92fa1273d60c4d Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Mon, 27 Apr 2020 15:51:55 -0400 Subject: [PATCH 17/18] scheduler: ensure changes to dns trigger update --- drivers/qemu/driver.go | 13 ++++++------- jobspec/test-fixtures/tg-network.hcl | 4 ++-- scheduler/util.go | 4 ++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index e119e94c6bf..f9c426e45dc 100644 --- a/drivers/qemu/driver.go +++ b/drivers/qemu/driver.go @@ -346,14 +346,14 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive "-nographic", } - var dnsArgs string + var netdevArgs []string if cfg.DNS != nil { if len(cfg.DNS.Servers) > 0 { - dnsArgs = ",dns=" + cfg.DNS.Servers[0] + netdevArgs = append(netdevArgs, "dns="+cfg.DNS.Servers[0]) } for _, s := range cfg.DNS.Searches { - dnsArgs = fmt.Sprintf("%s,dnssearch=%s", dnsArgs, s) + netdevArgs = append(netdevArgs, "dnssearch="+s) } } @@ -395,7 +395,6 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive // Loop through the port map and construct the hostfwd string, to map // reserved ports to the ports listenting in the VM // Ex: hostfwd=tcp::22000-:22,hostfwd=tcp::80-:8080 - var forwarding []string taskPorts := cfg.Resources.NomadResources.Networks[0].PortLabels() for label, guest := range driverConfig.PortMap { host, ok := taskPorts[label] @@ -404,14 +403,14 @@ func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drive } for _, p := range protocols { - forwarding = append(forwarding, fmt.Sprintf("hostfwd=%s::%d-:%d", p, host, guest)) + netdevArgs = append(netdevArgs, fmt.Sprintf("hostfwd=%s::%d-:%d", p, host, guest)) } } - if len(forwarding) != 0 { + if len(netdevArgs) != 0 { args = append(args, "-netdev", - fmt.Sprintf("user,id=user.0,%s%s", strings.Join(forwarding, ","), dnsArgs), + fmt.Sprintf("user,id=user.0,%s", strings.Join(netdevArgs, ",")), "-device", "virtio-net,netdev=user.0", ) } diff --git a/jobspec/test-fixtures/tg-network.hcl b/jobspec/test-fixtures/tg-network.hcl index 0aca76bf040..faaa79f94e5 100644 --- a/jobspec/test-fixtures/tg-network.hcl +++ b/jobspec/test-fixtures/tg-network.hcl @@ -14,8 +14,8 @@ job "foo" { } dns { - servers = ["8.8.8.8"], - options = ["ndots:2", "edns0"], + servers = ["8.8.8.8"] + options = ["ndots:2", "edns0"] } } diff --git a/scheduler/util.go b/scheduler/util.go index b0a3ed7c79c..97862a55ea2 100644 --- a/scheduler/util.go +++ b/scheduler/util.go @@ -445,6 +445,10 @@ func networkUpdated(netA, netB []*structs.NetworkResource) bool { return true } + if !reflect.DeepEqual(an.DNS, bn.DNS) { + return true + } + aPorts, bPorts := networkPortMap(an), networkPortMap(bn) if !reflect.DeepEqual(aPorts, bPorts) { return true From 7ce6c7b27bd2210de2a463a8851ef391d3e12c48 Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Mon, 27 Apr 2020 22:51:12 -0400 Subject: [PATCH 18/18] resolvconf: sync copied system resolv.conf before close --- drivers/shared/resolvconf/mount.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/shared/resolvconf/mount.go b/drivers/shared/resolvconf/mount.go index b154d67812b..02393478efc 100644 --- a/drivers/shared/resolvconf/mount.go +++ b/drivers/shared/resolvconf/mount.go @@ -73,7 +73,10 @@ func copySystemDNS(dest string) error { if err != nil { return err } - defer out.Close() + defer func() { + out.Sync() + out.Close() + }() _, err = io.Copy(out, in) return err