diff --git a/api/resources.go b/api/resources.go index cbd24c4bf813..fe5fb521538e 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 592d70bed2dd..6864b78e278f 100644 --- a/client/allocrunner/taskrunner/task_runner.go +++ b/client/allocrunner/taskrunner/task_runner.go @@ -940,6 +940,18 @@ func (tr *TaskRunner) buildTaskConfig() *drivers.TaskConfig { tr.networkIsolationLock.Lock() defer tr.networkIsolationLock.Unlock() + var dns *drivers.DNSConfig + 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, + } + } + } + return &drivers.TaskConfig{ ID: fmt.Sprintf("%s/%s/%s", alloc.ID, task.Name, invocationid), Name: task.Name, @@ -963,6 +975,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 09bac74cc2ef..9a20cd7244b6 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/command/assets/connect.nomad b/command/assets/connect.nomad index 59b6ca64354e..c7f6c6404ebb 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 { diff --git a/drivers/docker/driver.go b/drivers/docker/driver.go index 2b58b440ff84..90067cd3babb 100644 --- a/drivers/docker/driver.go +++ b/drivers/docker/driver.go @@ -885,6 +885,19 @@ func (d *Driver) createContainerConfig(task *drivers.TaskConfig, driverConfig *T hostConfig.ShmSize = driverConfig.ShmSize } + // 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 + 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 { @@ -948,9 +961,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/drivers/docker/driver_unix_test.go b/drivers/docker/driver_unix_test.go index 690a69e7367f..8aa362c94bd3 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.go b/drivers/exec/driver.go index 8d8b46e23b62..2a3cf4dba1c3 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/exec/driver_unix_test.go b/drivers/exec/driver_unix_test.go index 342993a8f4fc..e14f90c5790f 100644 --- a/drivers/exec/driver_unix_test.go +++ b/drivers/exec/driver_unix_test.go @@ -107,3 +107,61 @@ 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"}, + }, + }, + { + 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 { + 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/java/driver.go b/drivers/java/driver.go index 02931cdcf4ca..44ef04fa5dc0 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/java/driver_test.go b/drivers/java/driver_test.go index b4b3d0010c32..3d952c21abe2 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) + } + +} diff --git a/drivers/qemu/driver.go b/drivers/qemu/driver.go index f24bda73d668..f9c426e45dcd 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 netdevArgs []string + if cfg.DNS != nil { + if len(cfg.DNS.Servers) > 0 { + netdevArgs = append(netdevArgs, "dns="+cfg.DNS.Servers[0]) + } + + for _, s := range cfg.DNS.Searches { + netdevArgs = append(netdevArgs, "dnssearch="+s) + } + } + var monitorPath string if driverConfig.GracefulShutdown { if runtime.GOOS == "windows" { @@ -384,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] @@ -393,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", strings.Join(forwarding, ",")), + fmt.Sprintf("user,id=user.0,%s", strings.Join(netdevArgs, ",")), "-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 000000000000..02393478efcd --- /dev/null +++ b/drivers/shared/resolvconf/mount.go @@ -0,0 +1,83 @@ +package resolvconf + +import ( + "io" + "os" + "path/filepath" + + dresolvconf "github.com/docker/libnetwork/resolvconf" + "github.com/docker/libnetwork/types" + "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") + mount := &drivers.MountConfig{ + TaskPath: "/etc/resolv.conf", + HostPath: path, + Readonly: true, + PropagationMode: "private", + } + 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 mount, nil + } + + currRC, err := dresolvconf.Get() + if err != nil { + return nil, err + } + + var ( + 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, dnsList, dnsSearchList, dnsOptionsList) + if err != nil { + return nil, err + } + + return mount, nil +} + +func copySystemDNS(dest string) error { + in, err := os.Open(dresolvconf.Path()) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dest) + if err != nil { + return err + } + defer func() { + out.Sync() + out.Close() + }() + + _, err = io.Copy(out, in) + return err +} diff --git a/drivers/shared/resolvconf/mount_unix_test.go b/drivers/shared/resolvconf/mount_unix_test.go new file mode 100644 index 000000000000..76154f5909ba --- /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/jobspec/parse_network.go b/jobspec/parse_network.go index 6cd2ca6cbaf9..e72719309f2f 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 88fca2894c60..408bb2de45ed 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 538f49f3acfe..faaa79f94e5e 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/diff.go b/nomad/structs/diff.go index 34533c0584c9..31ea68380148 100644 --- a/nomad/structs/diff.go +++ b/nomad/structs/diff.go @@ -1069,6 +1069,50 @@ 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 { + diff.Type = DiffTypeAdded + newPrimitiveFlat = flatten(other) + } else if other == nil { + 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 ed6ea07970ce..4c0935c0d35f 100644 --- a/nomad/structs/diff_test.go +++ b/nomad/structs/diff_test.go @@ -2823,6 +2823,9 @@ func TestTaskGroupDiff(t *testing.T) { To: 8081, }, }, + DNS: &DNSConfig{ + Servers: []string{"1.1.1.1"}, + }, }, }, }, @@ -2865,6 +2868,18 @@ func TestTaskGroupDiff(t *testing.T) { }, }, }, + { + Type: DiffTypeAdded, + Name: "DNS", + Fields: []*FieldDiff{ + { + Type: DiffTypeAdded, + Name: "Servers", + Old: "", + New: "1.1.1.1", + }, + }, + }, }, }, { diff --git a/nomad/structs/network.go b/nomad/structs/network.go index 19aa47197cc3..f89508411f08 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 0880cbef067e..7c6f94a49c93 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -2214,16 +2214,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 835ec1c31d2e..05326486c6d3 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 77da6902a8fc..a071e5630e89 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 { @@ -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, + // 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, + 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, 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. diff --git a/plugins/drivers/proto/driver.proto b/plugins/drivers/proto/driver.proto index 59a0c10bb729..ec08a986b68c 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/testutils/dns_testing.go b/plugins/drivers/testutils/dns_testing.go new file mode 100644 index 000000000000..f27a8fead6c3 --- /dev/null +++ b/plugins/drivers/testutils/dns_testing.go @@ -0,0 +1,49 @@ +package testutils + +import ( + "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" +) + +// 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 := []byte(strings.TrimSpace(r.stdout)) + + if dns != nil { + 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) + 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)) + } + }) +} diff --git a/plugins/drivers/utils.go b/plugins/drivers/utils.go index c146e96ef80d..bfb3d5c2614b 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, + } +} diff --git a/scheduler/util.go b/scheduler/util.go index ee18e30007f0..acb01b9a01cc 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 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 000000000000..cdda554ba572 --- /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 000000000000..e348bc57f56b --- /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 000000000000..946bb87123e8 --- /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 000000000000..db1960c104e8 --- /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 000000000000..6a66aea5eafe --- /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 000000000000..8dada3edaf50 --- /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 000000000000..cfb675fd4ba4 --- /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 000000000000..574ececa8626 --- /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 000000000000..5adf982b086c --- /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 000000000000..3df30fa4601a --- /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 000000000000..94842f42702f --- /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 000000000000..ac340ddfbfa6 --- /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 000000000000..118fe159e92d --- /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 c0ef827100c2..58c456886dd2 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"}, @@ -284,6 +287,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"}, diff --git a/website/pages/docs/job-specification/network.mdx b/website/pages/docs/job-specification/network.mdx index 4f267b116a29..d362597d0230 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.